home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 1 / Cream of the Crop 1.iso / PROGRAM / LDB171.ARJ / LDB.DOC < prev    next >
Text File  |  1992-07-12  |  159KB  |  4,006 lines

  1. LDB171 is shareware meaning try before you buy.  If you use LDB171
  2. for other than evaluation purposes you are required to register.
  3. The registration fee is $60.  Upon registration you will be sent a
  4. hard copy manual and 3.5" DOS formatted diskette with the latest
  5. revisions.  Thank you for supporting the shareware concept!
  6.  
  7.  
  8.                       The Loose Data Binder
  9.  
  10.                       version 1.71, 7-11-92
  11.  
  12.                      for AT&T C++ 2.x & 3.x
  13.  
  14.  
  15.  
  16.                          Copyright 1992
  17.                           John W. Small
  18.                        All rights reserved
  19.  
  20.  
  21.  
  22.                       PSW / Power SoftWare
  23.                          P.O. Box 10072
  24.                    McLean, Virginia 22102 8072
  25.                                USA
  26.  
  27.  
  28.                       Voice: (703) 759-3838
  29.  
  30. Software License Agreement
  31.  
  32.      PSW / Power SoftWare licenses the bona fide purchaser of the
  33.      Loose Data Binder (LDB) to use the tool in the development of
  34.      software without royalty.  The licensee may not publish any
  35.      portion of the tool kit and may only make backup copies of
  36.      software and documentation as a means of protecting the
  37.      licensee's investment against loss or damage.
  38.  
  39.      A special consideration is made for LDB licensees desiring to
  40.      use LDB as a kernel of sorts within their commercially sold
  41.      software applications.  In such cases the licensee may include
  42.      portions of the LDB tool source code without royalty for the
  43.      sole purpose of allowing the licensee's customers the
  44.      convenience of being able to recompile the licensee's product
  45.      in various computing environments adding what is known in
  46.      programming parlance as "portability" to the licensee's
  47.      product.  All copyright notices in LDB source code must be
  48.      left unmodified.  The licensee's software in whole or part
  49.      must not be merely a new wrapping or packaging of the LDB tool
  50.      capability.  If such were the case a royalty bearing license
  51.      must be obtained.  The licensee must notify customers in the
  52.      licensee's product license agreement of this arrangement and
  53.      furthermore advise them that their subsequent direct use of
  54.      LDB apart from recompiling the licensee's product requires
  55.      them to purchase separate copies (licenses) of LDB.  The
  56.      requirement of notification can be satisfied by including the
  57.      following paragraph in the licensee's product license
  58.      agreement:
  59.  
  60.           Please be advised that portions of this software include
  61.           LDB source code which is licensed from John W. Small,
  62.           copyright owner of LDB.  Any subsequent direct use of LDB
  63.           source code apart from recompiling this product requires
  64.           you to purchase one or more copies of the LDB tool.
  65.  
  66.      Special permission is hereby granted to the computer science
  67.      media (press) and its authors to publish any portion of the
  68.      header files comprising the LDB in reviews or articles.  Thus
  69.      articles invoking the LDB interface may be published without 
  70.      specific permission.
  71.  
  72.      Likewise special permission is hereby granted to instructors
  73.      in public and private learning institutions to use LDB for
  74.      purposes of classroom instruction.  Contact PSW for details on
  75.      LDB classroom materials. Limited Warranty
  76.  
  77.      PSW / Power SoftWare warrants the Loose Data Binder toolkit to
  78.      be free from defects in materials and workmanship for a period
  79.      of 90 days from the original purchase date and will replace
  80.      same if found to be defective and reported in writing to PSW
  81.      within this period.  The fitness of the Loose Data Binder
  82.      toolkit for any particular application is not implied nor
  83.      guaranteed.  All other liabilities which may be construed are
  84.      specifically disclaimed except where and when required by law.
  85.  
  86. Registration
  87.  
  88.      Please fill out and return the registration post card. 
  89.      Notices of updates and new example diskettes are mailed from
  90.      the registration roster.  Thank you!
  91.  
  92. Technical Support
  93.  
  94.      Have a question, comment, or suggestion?  I'm looking forward
  95.      to hearing from you.  A comment post card has been included
  96.      for your convenience.  If hard copy mail is too slow you can
  97.      contact me directly as indicated below.
  98.  
  99.                John W. Small
  100.                Voice: (703) 759-3838
  101.  
  102. Acknowledgments
  103.  
  104.      I wish to thank my Lord and Savior, Jesus Christ, who give
  105.      himself for me, so that I might receive God's gift of
  106.      reconciliation and eternal life.
  107.                        Table of Contents
  108.  
  109.  
  110.  
  111. Introduction . . . . . . . . . . . . . . . . . . . . . . . . .  7
  112.  
  113. Chapter 1:  Installation . . . . . . . . . . . . . . . . . . .  9
  114.  
  115. Chapter 2:  Type Checking. . . . . . . . . . . . . . . . . .   11
  116.      Typeless Binders. . . . . . . . . . . . . . . . . . . .   11
  117.      Template Binders. . . . . . . . . . . . . . . . . . . .   12
  118.      Form "Templates". . . . . . . . . . . . . . . . . . . .   13
  119.      Form Cookbook . . . . . . . . . . . . . . . . . . . . .   14
  120.      Summary . . . . . . . . . . . . . . . . . . . . . . . .   15
  121.  
  122. Chapter 3:  Hybrid Container . . . . . . . . . . . . . . . .   17
  123.      Elastic-Array Category. . . . . . . . . . . . . . . . .   17
  124.      Stack-Queue-Deque Category. . . . . . . . . . . . . . .   19
  125.      List Category . . . . . . . . . . . . . . . . . . . . .   20
  126.      Sort-Search-Unique Category . . . . . . . . . . . . . .   22
  127.      Summary . . . . . . . . . . . . . . . . . . . . . . . .   23
  128.  
  129. Chapter 4:  Assigning, Cloning, and Deleting . . . . . . . .   25
  130.      Summary . . . . . . . . . . . . . . . . . . . . . . . .   29
  131.  
  132. Chapter 5:  Persistence. . . . . . . . . . . . . . . . . . .   31
  133.      Summary . . . . . . . . . . . . . . . . . . . . . . . .   39
  134.  
  135. Chapter 6:  Mutually Owned Containers and Nodes. . . . . . .   41
  136.      Summary . . . . . . . . . . . . . . . . . . . . . . . .   54
  137.  
  138. Chapter 7:  Reference. . . . . . . . . . . . . . . . . . . .   55
  139.      Binder. . . . . . . . . . . . . . . . . . . . . . . . .   55
  140.      MBinder . . . . . . . . . . . . . . . . . . . . . . . .   87
  141.      Mutual. . . . . . . . . . . . . . . . . . . . . . . . .   92
  142.      StreamRegistry. . . . . . . . . . . . . . . . . . . . .  101
  143.  
  144. Index. . . . . . . . . . . . . . . . . . . . . . . . . . . .  105
  145.                         Introduction
  146.  
  147.  
  148. The Loose Data Binder, LDB, is an unconventional persistent
  149. container class having a hybrid stack, queue, priority queue,
  150. deque, list, array interface and built-in sort, search, and iterate
  151. functions in one flat class.  Being persistent, an LDB can be saved
  152. on a stream and later reloaded while multiple references to its
  153. various elements are automatically resolved, thus allowing complex
  154. container networks to persist without a second thought.  I think
  155. that you will find LDB a lot more efficient, convenient, and
  156. comprehensible than conventional OOP container class libraries with
  157. their towering, convoluted hierarchies.  And LDB's unconventional
  158. hybrid structure allows you to implement practical algorithms which
  159. conventional "textbook" libraries can't touch!
  160.  
  161. Think of the LDB as an elastic array of void pointers pointing to
  162. whatever data you wish to bind.  Unlike a conventional C++ array
  163. that is statically sized, the LDB can expand or contract
  164. automatically to accommodate varying numbers of cells.  You control
  165. the granularity and hysteresis of this elasticity.  And of course
  166. you can impose strong type checking for worry free plug and play
  167. containers for all your data types using templates or forms if your
  168. compiler doesn't support templates.
  169.  
  170. When you use LDB's for all your linked list and container needs,
  171. your applications will require less design effort, less
  172. documentation, less testing, less code space, less programming
  173. time, and less maintenance effort.  And PSW's OEM friendly license
  174. agreement is a real boon to commercial class designers.
  175.                           Chapter 1
  176.  
  177.                           Installation
  178.  
  179.  
  180. If you are a DOS user, insert the LDB distribution diskette in the
  181. "A" drive and type A:\install <enter>.  Otherwise, copy the
  182. following files to the appropriate directories for the C++ compiler
  183. and operating system you are using.
  184.  
  185.  
  186.      binder.hpp          Binder class (basic LDB class)
  187.      binder.cpp
  188.  
  189.      tbinder.hpp         template
  190.  
  191.      fbinder.hpp         form for strong type checking
  192.      fbinder.cpp         without templates
  193.  
  194.      mutual.hpp          mutually owned containers and nodes
  195.      mutual.cpp          
  196.  
  197.      fmutual.hpp         form for deriving mutually owned
  198.      fmutual.cpp         heterogeneous nodes
  199.  
  200.  
  201. You may need to rename the extensions of these files to suit your
  202. compiler.  LDB source code is AT&T C++ version 2.x, 3.x compatible.
  203.  
  204. For those of you who don't have smart linker/librarians on your
  205. system, the Loose Data Binder source is broken down into individual
  206. member functions and grouped into appropriate files to facilitate
  207. the building of libraries.  These files can be found in the
  208. "libsrc.cpp" subdirectory on the distribution diskette.  You will
  209. find a makefile in this subdirectory.  Edit the macros in the
  210. makefile to suit your environment.  A LDB library can be quickly
  211. built for your compiler using this makefile.
  212.  
  213. The examples found in the text are grouped together in the
  214. "examples.cpp" subdirectory.
  215.  
  216. Be sure to read any readme.doc files that may appear on the
  217. distribution diskette for last minute revisions.
  218.                           Chapter 2
  219.  
  220.                           Type Checking
  221.  
  222.  
  223. The Loose Data Binder's basic container class is named Binder.  The
  224. Binder class used by itself performs no type checking on the data
  225. being bound.  However, by using templates a Binder can be made to
  226. impose strong type checking.  Even if your compiler doesn't support
  227. templates, strong type checking can still be achieved with forms. 
  228. Forms can also be used for data types not meeting the template
  229. requirements.
  230.  
  231.  
  232.                         Typeless Binders
  233.  
  234.  
  235. Sometimes you may have a need for a typeless binder, e.g. binding
  236. heterogenous data in the same container.  A disadvantage of
  237. typeless binders is that they can not be expected to be persistent
  238. since nothing is known about the type of data bound within.  The
  239. following trivial program queues up five dynamically allocated
  240. integers.  Then the "list" is walked while displaying each node's
  241. contents.  When the Binder is destructed, its nodes are deleted. 
  242. Please note that any combination of data types could have been
  243. queued into a typeless Binder, not just integers.
  244.  
  245.  
  246.      // examp201.cpp - link with binder.obj
  247.  
  248.      #include "binder.hpp"
  249.  
  250.      main()
  251.      {
  252.           Binder b(BDR_DDELETE,5);
  253.  
  254.           for (int i = 0; b.insQ(new int(i)); i++);
  255.  
  256.           while (++b)
  257.                cout << *(int *)b.get() << "\n";
  258.  
  259.           return 0;
  260.      }
  261.                        Template Binders
  262.  
  263.  
  264. Suppose that we wanted to insure that only integers were bound in
  265. the Binder.  One way is to use C++ templates as follows:
  266.  
  267.  
  268.      // examp202.cpp - link with binder.obj
  269.  
  270.      #include "tbinder.hpp"
  271.      TBINDER(int,IntBdr,IntBdR);
  272.  
  273.      main()
  274.      {
  275.           IntBdr b(BDR_DDELETE|BDR_DNEW,5);
  276.  
  277.           for (int i = 0; b.insQNew(&i); i++);
  278.  
  279.           while (++b)
  280.                cout << *(int *)b << "\n";
  281.  
  282.           return 0;
  283.      }
  284.  
  285.  
  286. To create a strongly type checked "int" Binder we include the
  287. tbinder.hpp file and invoke the TBINDER() macro to generate the
  288. template instance.  TBINDER()'s first parameter is the type of data
  289. the container is to hold.  The second is the name of the Binder
  290. type that will perform the type checking, and the third is the name
  291. of a pointer to this new Binder type.
  292.  
  293. Data types are required to have:
  294.  
  295.      a default constructor,
  296.      a copy initializer constructor, i.e. int(int&),
  297.      a destructor if one is necessary,
  298.      an assignment operator, i.e. operator=(),
  299.      an overloaded stream insertion operator,
  300.           i.e. ostream& operator<<(ostream&,int&),
  301.      and a stream extraction operator,
  302.           i.e. istream& operator>>(istream&,int&)!
  303.  
  304. Built in types such as int, float, double, etc. implicitly fulfill
  305. these requirements and can thus be readily used with templates.  
  306.                        Form "Templates"
  307.  
  308.  
  309. Strong type checking can be achieved without templates by using
  310. forms.  Data types must meet the same template imposed requirements
  311. to be used with forms in this manner.
  312.  
  313.  
  314.      // examp203.cpp - link with binder.obj
  315.  
  316.      #define   FType    int
  317.      #define   FBinder  IntBdr
  318.      #define   FBindeR  IntBdR
  319.  
  320.      #include  "fbinder.hpp"
  321.  
  322.      main()
  323.      {
  324.           IntBdr b(BDR_DDELETE|BDR_DNEW,5);
  325.  
  326.           for (int i = 0; b.insQNew(&i); i++);
  327.  
  328.           while (++b)
  329.                cout << *(int *)b << "\n";
  330.  
  331.           return 0;
  332.      }
  333.  
  334.  
  335. The tbinder.hpp file is no longer used.  This time we define FType,
  336. FBinder, FBindeR instead of invoking the TBINDER() macro.  By
  337. including the fbinder.hpp file after these macros, fbinder.hpp in
  338. effect generates a "form" instance in a manner similar to a
  339. template instance.  The inclusion of fbinder.hpp leaves FType,
  340. FBinder, and FBindeR undefined so that the procedure can be used
  341. repeatedly to instantiate various Binder types.
  342.                          Form Cookbook
  343.  
  344.  
  345. You can also use forms to create strongly type checked Binders for
  346. data types that do not have the prerequisites for templates or form
  347. "templates."  For sake of argument, suppose the "int" type did not
  348. meet the requirements for templates or form "templates."  Simply
  349. copy fbinder.hpp to intbdr.hpp and fbinder.cpp to intbdr.cpp. 
  350. Globally replace FType with int, FBinder with IntBdr, and FBindeR
  351. with IntBdR in both files.  If "int" really didn't have the
  352. prerequisites we would proceed to edit the intbdr.cpp file as
  353. follows:
  354.  
  355.  
  356.      edit function:      to perform:
  357.  
  358.      Dassign()           assignment
  359.      Dnew()              copy-initialize construction
  360.      Ddelete()           destruction
  361.      Dstore()            stream insertion
  362.      Dload()             stream extraction
  363.  
  364.  
  365. With intbdr.hpp and intbdr.cpp now ready we can proceed with the
  366. example:
  367.  
  368.  
  369.      // examp204.cpp - link with intbdr.obj and binder.obj
  370.  
  371.      #include  "intbdr.hpp"
  372.  
  373.      main()
  374.      {
  375.           IntBdr b(BDR_DDELETE|BDR_DNEW,5);
  376.  
  377.           for (int i = 0; b.insQNew(&i); i++);
  378.  
  379.           while (++b)
  380.                cout << *(int *)b << "\n";
  381.  
  382.           return 0;
  383.      }
  384.                             Summary
  385.  
  386. Binders used by themselves preform no type checking and are not
  387. persistent, but they are useful for holding heterogeneous data
  388. types.  Templates are used to create Binders with strong type
  389. checking.  If your compiler doesn't support templates, form
  390. "templates" can be used in a manner similar to templates.  Forms
  391. can also be cloned and edited for use with data types that do not
  392. have all the prerequisites for templates or form "templates", i.e.
  393. default constructor, copy initializer constructor, destructor,
  394. assignment operator, and overloaded stream insertion and extraction
  395. operators.  Remember, all strongly typed checked Binders can
  396. persist!
  397.                           Chapter 3
  398.  
  399.                         Hybrid Container
  400.  
  401.  
  402. The next step to learning to program with the LDB tool is to
  403. mentally categorize the multifaceted behavior of the Binder class. 
  404. Let's call the first category "elastic-array."  The elastic-array
  405. concept provides the foundation for the rest of the inherent
  406. behaviors of a Binder.  Let's call the second category "stack-
  407. queue-deque", the third "list", and the last "sort-search-unique." 
  408. Remember, a Binder can exhibit any or all of these behaviors at any
  409. time which is why it is referred to as an unconventional flat
  410. container class.
  411.  
  412.  
  413.                      Elastic-Array Category
  414.  
  415.  
  416. Imagine a C++ array (vector) of void pointers that can be declared
  417. to have any number of cells.  You can read or write a pointer value
  418. to any cell in this array.  Okay, nothing spectacular so far.  But
  419. now take this same array and insert new cells or extract old ones
  420. at any location on the fly and you have an elastic array.  The
  421. Binder class allows you to specify the granularity and hysteresis
  422. of expansion and contraction.  In other words, when a new cell
  423. needs to be added how many additional spare cells should be
  424. provided to optimize future expansion efforts?  How many spare
  425. cells should be allowed to accumulate before contraction?  You can
  426. specify this granularity and hysteresis with the delta parameter in
  427. either a constructor call or housekeeping function.  Add an
  428. overloaded subscript operator and forEach iterator and you have a
  429. handy little array class.  The following list of functions catalogs
  430. this elastic-array behavior:
  431.  
  432.  
  433.      vector()            Limit()             setLimit()
  434.      pack()              Delta()             setDelta()
  435.      Nodes()             MaxNodes()          setMaxNodes()
  436.      vacancy()           vacancyNonElastic()
  437.      atIns()             atInsNew()          atRmv()
  438.      allRmv()            atDel()             atDelAsg()
  439.      allDel()            atPut()             atPutNew()
  440.      atPutAsg()          atGet()             operator[]()
  441.      atGetAsg()          atXchg()            index()
  442.      forEach()
  443. Elastic array operation is really summed up by two primitives,
  444. atIns() and atRmv().  AtIns() writes a pointer value to a newly
  445. inserted cell in the Binder's logical array at the index indicated. 
  446. The cell originally at the index and all successive cells are
  447. pushed back one index location, expanding the physical array if
  448. necessary.  AtRmv() performs the inverse function of atIns(),
  449. extracting the indexed cell returning its pointer while pulling all
  450. successive cell indices forward by one.  The physical array is
  451. shrunk automatically if excessive space has accumulated.
  452.  
  453. The following example shows a typeless Binder used to sort a vector
  454. of strings.  Notice the use of typecasts for function pointers as
  455. well as for data pointers.
  456.  
  457.  
  458.      // examp301.cpp - link with binder.obj
  459.  
  460.      #include <string.h>
  461.      #include "binder.hpp"
  462.  
  463.      char *v[] = {
  464.           "Vectors can be",
  465.           "'exploded' into Binders",
  466.           "sorted and then",
  467.           "'imploded' back to vectors!", 
  468.           0
  469.      };
  470.  
  471.      main()
  472.      {
  473.           Binder b ((voiDV) v);  // explode constructor
  474.  
  475.           cout << "\n\nExploded, unsorted vector ...\n\n";
  476.           for (unsigned i = 0; b[i]; i++)
  477.                cout << (char *) b[i] << "\n";
  478.           b.sort ((BDRcomP) strcmp);    // sort
  479.           char **sV = (char **) b.vector();  // implode
  480.           if (!sV)  return 1;
  481.           cout << "\n\nImploded, sorted vector ... \n\n";
  482.           for (i = 0; sV[i]; i++)  
  483.                     cout << sV[i] << "\n";
  484.           delete sV;
  485.           return 0;
  486.      }
  487.                   Stack-Queue-Deque Category
  488.  
  489.  
  490. A stack provides LIFO (Last In, First Out) storage.  The Binder has
  491. a complete set of stack primitives.  These functions don't disturb
  492. list structure of the LDB, e.g. current node setting.  A queue
  493. provides FIFO (First In, First Out) storage.  And the deque
  494. provides FOLO (First Out, Last Out) storage.  Think of the deque as
  495. a combination stack-queue with the additional ability of being able
  496. to pop the rear of the queue.  The following list of Binder
  497. functions support this stack-queue-deque structure:
  498.  
  499.  
  500.      push()              pushNew()           pop()
  501.      popDel()            popDelAsg()         top()
  502.      topAsg()            insQ()              insQNew()
  503.      unQ()               unQDel()            unQDelAsg()
  504.      rear()              rearAsg()           operator<<()
  505.      operator>>()
  506.  
  507.  
  508. The following example shows a form Binder for floats being built
  509. like a stack and unloaded as a deque.  UnQDelAsg() copies from the
  510. last node to the address given before deleting the node.
  511.  
  512.  
  513.      // examp302.cpp - link with binder.obj
  514.  
  515.      #define   FType    float
  516.      #define   FBinder  fbdr
  517.      #define   FBindeR  fbdR
  518.  
  519.      #include  "fbinder.hpp"
  520.  
  521.      main()    // count to 5
  522.      {
  523.           fbdr fb(BDR_DASSIGN|BDR_DNEW|BDR_DDELETE,5);
  524.  
  525.           for (float f = 1.0; fb.pushNew(&f); f++);
  526.  
  527.           while (fb.unQDelAsg(&f))
  528.                cout << f << "\n";
  529.  
  530.           return 0;
  531.      }
  532.                          List Category
  533.  
  534.  
  535. Any Binder can be treated as if it were a doubly linked list
  536. thereby providing sequential storage.  In a Binder's header, a
  537. current node index is maintained that lets the list primitives know
  538. where the insertion or removal is to take place.  The elastic-array
  539. and stack-queue-deque primitives don't disturb this current node
  540. index unless of course it points to the cell that's being removed. 
  541. In that case the current node becomes undefined just like it was
  542. when the Binder was first constructed.  Here's a list of the list
  543. primitives:
  544.  
  545.  
  546.      CurNode()           setCurNode()        ins()
  547.      insNew()            rmv()               del()
  548.      delAsg()            put()               putNew()
  549.      putAsg()            get()               getAsg()
  550.      next()              operator++()        nextAsg()
  551.      prev()              operator--()        prevAsg()
  552.      firstThat()         lastThat()
  553.  
  554.  
  555. The following example shows a trivial structure designed to be use
  556. with templates or form "templates."  It has default and copy
  557. initializer constructors, destructor, assignment and overloaded
  558. stream insertion and extraction operators.  It is use here to
  559. generate a Binder template instance.
  560.  
  561.  
  562.      // examp303.cpp - link with binder.obj
  563.  
  564.      #define fsbfile  "examp203.txt"
  565.  
  566.      #include <iostream.h>
  567.      #include <iomanip.h>
  568.  
  569.      struct FS {
  570.           float f;
  571.           FS(float f = 0.0)  { this->f = f; }
  572.           FS(FS& fs)  { f = fs.f; }
  573.           FS& operator=(FS& fs)  { f = fs.f; return fs; }
  574.           ~FS()  {}
  575.      };
  576.      inline ostream& operator<<(ostream& os, FS& fs)
  577.        { return os << fs.f; }
  578.      inline istream& operator>>(istream& is, FS& fs)
  579.        { return is >> fs.f; }
  580.  
  581.      #include "tbinder.hpp"
  582.      TBINDER(FS,FSbdr,FSbdR);
  583.  
  584.      int fcmp(const FS *F1, const FS * F2)
  585.        { return (F2->f - F1->f); }
  586.  
  587.      main()    // count to five
  588.      {
  589.        FSbdR FSB = new FSbdr (BDR_DNEW | BDR_DDELETE
  590.           | BDR_DSTORE,5);
  591.  
  592.        if (!FSB)  return 1;
  593.  
  594.        FS fs = 1.0;
  595.  
  596.        while (FSB->insNew(&fs))  fs.f++;
  597.  
  598.        FSB->setComP ((BDRcomP) fcmp);
  599.        Binder::RegisterComP ((BDRcomP) fcmp);
  600.  
  601.        FSB->save(fsbfile);
  602.  
  603.        delete FSB;
  604.  
  605.        FSbdr fsb(fsbfile);
  606.  
  607.        fsb.sort();
  608.  
  609.        fsb.setFlags(BDR_DASSIGN);
  610.  
  611.        while (fsb.prevAsg(&fs))
  612.             cout << fs.f << endl;
  613.  
  614.        return 0;
  615.      }
  616. The first list is dynamically allocated and limited to five nodes
  617. by its constructor.  The ins()/insNew() functions make their
  618. insertions after the current node making the newly inserted node
  619. current.  In the first iteration the current node is undefined so
  620. the new node is simply placed at the end of the list and made the
  621. current node.  Note that the compare function is coded for
  622. descending order!  The Binder compare function is set to one that
  623. compares float structures and gets saved on the stream.  Compare
  624. functions must be registered to be persistent!  After the Binder is
  625. reloaded the BDR_DASSIGN flag is set so that primitives with "Asg"
  626. suffixes are enabled.  This list is walked backwards after being
  627. sorted in descending order, in order to count to five.
  628.  
  629.  
  630.                    Sort-Search-Unique Category
  631.  
  632.  
  633. Binders can also be sorted and searched on a supplied compare
  634. function.  These primitives affect the list's current node setting.
  635.  
  636.  
  637.      Sorted()            unSort()            setComP()
  638.      ComP()              sort()              insSort()
  639.      insSortNew()        insUnique()         insUniqueNew()
  640.      findFirst()         findNext()          findLast()
  641.      findPrev()          findAll()
  642.  
  643.  
  644. With these primitives you can build bags, sets, dictionaries, etc. 
  645. Our demo is a trivial exercise of the setComP(), insSort(), and
  646. findAll() primitives.  Pointers to the integers are inserted in
  647. sorted order using the supplied intcmp() compare function. 
  648. FindAll() uses the same compare function to count matches.
  649.  
  650.  
  651.      // examp304.cpp - link with binder.obj
  652.  
  653.      #include "binder.hpp"
  654.  
  655.      int v[] = {  25, 3, 43, 17, 7, 35, 3 };
  656.  
  657.      int intcmp(const int *I1, const int *I2)
  658.           { return (*I1 - *I2); }
  659.  
  660.     main()
  661.      {
  662.           Binder b;
  663.  
  664.           b.setComP ((BDRcomP) intcmp);
  665.           for (int i = 0; i < sizeof(v)/sizeof(v[0]); i++)
  666.                b.insSort ((voiD) &v[i]);
  667.           i = 3;
  668.           cout << "\n" << b.findAll(&i) << "   " << i 
  669.                << "'s are present in the following "
  670.                "sort Binder.\n\n";
  671.           while (b.next())
  672.                cout << *(int *)b.get() << "\n";
  673.           return 0;
  674.      }
  675.  
  676.  
  677.                              Summary
  678.  
  679. The easiest way to conceptualize Binder operations is to classify
  680. its primitive behaviors into four categories: elastic-array, stack-
  681. queue-deque, list, and sort-search-unique.  A Binder has an
  682. inherent current node setting associated with its list behavior
  683. which is also modified by its sort-search-unique primitives but
  684. left undisturbed by the elastic-array and stack-queue-deque
  685. primitives.
  686.  
  687.                            Chapter 4
  688.  
  689.                 Assigning, Cloning, and Deleting
  690.  
  691.  
  692. In the last chapter you were exposed to Binder primitives with
  693. "Asg", "New", and "Del" suffixes and "del" within their names. 
  694. These primitives perform on the fly assigning, cloning, and
  695. deleting of your data types, respectively, but otherwise they work
  696. essentially the same as their namesake counterparts.  The
  697. BDR_DASSIGN, BDR_DNEW, and BDR_DDELETE flags each have to be set to
  698. enable their respective "Asg", "New", and "Del/del" functions. 
  699. Only strongly typed checked Binders know about the data types bound
  700. within, so even though you may set these flags in typeless Binders,
  701. their corresponding functions are left disabled except for the
  702. "Del/del" primitives.
  703.  
  704. For example, take the next() and nextAsg() primitives.  Next()
  705. advances the list's current node by one returning a pointer to the
  706. new current node.  NextAsg() does the same thing, but it also
  707. copies the node to the destination address given as a parameter.
  708.  
  709.  
  710.      // examp401.cpp - link with binder.obj
  711.  
  712.      #include "tbinder.hpp"
  713.      TBINDER(int,IntBdr,IntBdR);
  714.  
  715.      main()
  716.      {
  717.           IntBdr b (BDR_DASSIGN | BDR_DNEW | BDR_DDELETE, 5);
  718.  
  719.           for (int i = 0; b.insQNew(&i); i++);
  720.  
  721.           while (b.nextAsg(&i))  cout << i << "\n";
  722.  
  723.           b.allDel();
  724.  
  725.           return 0;
  726.      }
  727.  
  728.  
  729. This example also shows the insQNew() cloning an integer and
  730. inserting the pointer to the clone instead of to the original. 
  731. AllDel() is called to remove all nodes from the Binder though this
  732. is really not necessary since the Binder destructor will call it  automatically if BDR_DDELETE flag is set.  If you set BDR_DNEW you
  733. will most likely want to set BDR_DDELETE also.  If you set
  734. BDR_DDELETE and don't want the Binder's destructor to delete your
  735. nodes, you could either call allRmv() or reset the BDR_DDELETE flag
  736. prior to the destructor call.  AllRmv() removes all cells from the
  737. elastic array discarding the pointers without deleting them. 
  738. Binder "Asg", "New", and "Del/del" primitives invoke the
  739. assignment, copy initializer, and destruct operations,
  740. respectively, on the data type being stored within the Binder. 
  741. This is accomplished via the Binder's Dassign(), Dnew(), and
  742. Ddelete() protected virtual functions.  In a typeless Binder,
  743. Dassign() and Dnew() do nothing since the data type is unknown. 
  744. Thus the "Asg" and "New" primitives are disabled.  In a strongly
  745. type checked Binder they are able to correctly invoke the proper
  746. operations.
  747.  
  748. C++ strings can't be used as is with Binder templates or form
  749. "templates" because they don't have a meaningful default
  750. constructor or copy initializer.  The following example
  751. encapsulates a C++ string in a type that can be used with either a
  752. Binder template or form "template."
  753.  
  754.  
  755.      // examp402.cpp - link with binder.obj
  756.  
  757.      #include <string.h>
  758.      #include <iostream.h>
  759.  
  760.      struct str {
  761.        char *s;
  762.        str(const char *cs = (char *)0)  // default constructor
  763.           { s = (cs? strdup(cs) : (char *)0); }
  764.        str(str& si)                     // copy initializer
  765.             { s = (si.s? strdup(si.s) : si.s); }
  766.        void operator=(str& si)          // assignment
  767.             { delete s; s = (si.s? strdup(si.s) : si.s); }
  768.        void operator=(const char * cs)
  769.             { delete s; s = (cs? strdup(cs) : (char *)0); }
  770.        ~str() { delete s; }             // destructor
  771.      };
  772.  
  773.      ostream& operator<<(ostream& os, str&)
  774.        { return os; }
  775.  
  776.      istream& operator>>(istream& is, str&)
  777.        { return is; }
  778.  
  779.      #include "tbinder.hpp"
  780.      TBINDER(str,StrBdr,StrBdR);
  781.  
  782.      main()
  783.      {
  784.        StrBdr sb(BDR_DDELETE);
  785.  
  786.        sb.ins(new str("line one"));
  787.        str s("Why won't this string appear in the Binder?");
  788.        sb.insNew(&s);
  789.        s = "line two";
  790.        sb.setFlags(BDR_DNEW);
  791.        sb.insNew(&s);
  792.        sb.setCurNode();       // reset current node
  793.        while (sb.next())  cout << ((str *)sb)->s << "\n";
  794.        return 0;
  795.      }
  796.  
  797.  
  798. The first insNew() gracefully fails because the BDR_DNEW flag is
  799. not set.  That's remedied before the second attempt.  Both template
  800. and form "template" Binder instances have the implicit type cast
  801. operator defined as returning a pointer to the current node.  The
  802. type of the typecast is of course the type being bound within the
  803. Binder, e.g. (str *).
  804.  
  805. We can gain more insight into the operation of the Dassign(),
  806. Dnew(), and Ddelete() virtual functions by considering how we can
  807. alternatively adapt a form Binder instance to accommodate C++
  808. strings instead of the using the wrapper structure of the previous
  809. example.  We can't just use a template by invoking TBINDER() with
  810. "char *" as its first parameter.  Nor can we use form "templates"
  811. for the same reason.  The solution is to copy fbinder.hpp to
  812. strbdr.hpp and fbinder.cpp to strbdr.cpp and globally replace FType
  813. with char, FBinder with StrBdr, FBindeR with StrBdR in both files.
  814.  
  815. Strbdr.cpp is additionally edited to allow for assigning strings
  816. via the Binder's overridden Dassign() virtual function.  Dassign()
  817. is used by all Binder primitives with the "Asg" suffix in their
  818. names to assign data to or from its bound nodes and the outside
  819. world.  Our string assignment overwrites the destination string up
  820. to its terminating '\0' or for the length of the source string,
  821. which ever is shorter.
  822.     voiD StrBdr::Dassign(voiD D, const voiD S)
  823.      {
  824.           while (*(char *)D)
  825.                if ((*((char *)D)++ = *((char *)S)++ )
  826.                     == '\0')  break;
  827.           return D;
  828.      }
  829.  
  830.  
  831. The Binder primitives will never call Dassign() unless the
  832. BDR_DASSIGN flag is set.  Also Dassign()'s parameters are
  833. guaranteed never to be NULL!  If Dassign() returns NULL the calling
  834. Binder primitive is obliged to leave the Binder in an unchanged
  835. state.  For example,  Binder::nextAsg() will leave the current node
  836. setting untouched if your Dassign() returns NULL.  Thus Binder
  837. assignment operations can be inhibited by either failing to set the
  838. BDR_DASSIGN flag or overriding Dassign() to always return NULL. 
  839. Please note that the strong type checking is performed by the
  840. Binder's public primitives invoking Dassign() and the various other
  841. protected virtual functions and not by the virtual functions
  842. themselves.  After all you can't override a virtual function unless
  843. its prototype is identical to the original virtual function.
  844.  
  845. String cloning has been provided for by overriding the Binder's
  846. Dnew() virtual function.  Dnew() is used by all Binder primitives
  847. with the "New" suffix in their names to clone data on the fly,
  848. binding the clone instead of the original data as is usually the
  849. case.  Dnew() performs essentially what a copy initializer
  850. constructor would, in our case it calls the standard C library
  851. string function, strdup().
  852.  
  853.  
  854.      voiD StrBdr::Dnew(const voiD D)
  855.           { return  (voiD) strdup((char *)D); }
  856.  
  857.  
  858. The Binder primitives will never call Dnew() unless the BDR_DNEW
  859. flag is set.  Again, Dnew()'s parameter is guaranteed never to be
  860. NULL.  If your Dnew() returns NULL the Binder is left in an
  861. unaltered state.  Thus Binder cloning operations can be inhibited
  862. by either failing to set the BDR_DNEW flag or overriding Dnew() to
  863. always return NULL.
  864.  
  865. The Binder's Ddelete() virtual function provides a combination
  866. destruction - deallocation service for Binder primitives having
  867. "del" or "Del" in their names.  Ddelete() will never be called
  868. unless the BDR_DDELETE flag is set and there is something to
  869. delete, i.e. Ddelete()'s parameter is never NULL.  Our Ddelete()
  870. for strings simply deallocates memory.
  871.  
  872.  
  873.      void StrBdr::Ddelete(voiD D)
  874.           { delete (char *) D; }
  875.  
  876.  
  877. You can inhibit all Binder deletion operations by failing to set
  878. the BDR_DDELETE flag.  With strbdr.hpp and strbdr.cpp now ready we
  879. can redo the last example as follows.
  880.  
  881.  
  882.      // examp403.cpp - link with strbdr.obj and binder.obj
  883.  
  884.      #include <string.h>
  885.      #include "strbdr.hpp"
  886.  
  887.      main()
  888.      {
  889.        StrBdr sb(BDR_DDELETE | BDR_DNEW);
  890.  
  891.        sb.ins(strdup("line one"));
  892.        sb.insNew("line two");
  893.        sb.setCurNode();
  894.        while (sb.next())  cout << (char *)sb << "\n";
  895.        return 0;
  896.      }
  897.  
  898.  
  899.                              Summary
  900.  
  901. The Binder "Asg", "New", and "Del/del" primitives perform on the
  902. fly assignment, cloning, and deletion, respectively, on the data
  903. elements of a strongly type checked Binder.  These primitives are
  904. enabled by setting the BDR_DASSIGN, BDR_DNEW, and BDR_DDELETE
  905. flags.  When these flags are set in a typeless Binder their
  906. corresponding primitives remain disabled except for "Del/del"
  907. primitives.  By defining a form instance's Dassign() and Dnew()
  908. virtual functions to return NULL the "Asg" and "New" primitives can
  909. be permanently disabled regardless of the flags settings.
  910.  
  911.                            Chapter 5
  912.  
  913.                            Persistence
  914.  
  915.  
  916. Persistence refers to the ability of a Binder to persistent beyond
  917. the termination of the program in which it exists.  In other words,
  918. Binder objects can be made to persist between program invocations. 
  919. This view of a data object hides the details of the implementation
  920. of its file record structure, encapsulating it within the object
  921. where OOP rightly says it belongs.
  922.  
  923. You saw an example of a persistent float Binder in examp303.cpp
  924. back in chapter 3.  Since floats can be used directly with
  925. templates or forms, that example was overkill.  It was used, among
  926. other things, to demonstrate how to make your struct or class
  927. template-form aware.  Let's rework that example making it simpler
  928. but using forms this time.  Recalling from before, please note that
  929. the compare function is coded for descending order.
  930.  
  931.  
  932.      // examp501.cpp - link with binder.obj
  933.      // rework of examp303.cpp
  934.  
  935.      #define ffile  "examp501.txt"
  936.  
  937.      #include <iostream.h>
  938.      #include <iomanip.h>
  939.  
  940.      #define   FType    float
  941.      #define   FBinder  fbdr
  942.      #define   FBindeR  fbdR
  943.  
  944.      #include  "fbinder.hpp"
  945.  
  946.      int fcmp(const float *f1, const float * f2)
  947.        { return (*f2 - *f1); }  // descending order
  948.      main()    // count to five
  949.      {
  950.        fbdR fB = new fbdr (BDR_DNEW | BDR_DDELETE
  951.           | BDR_DSTORE,5);
  952.  
  953.        if (!fB)  return 1;
  954.  
  955.        float f = 1.0;
  956.  
  957.        while (fB->insNew(&f))  f++;
  958.  
  959.        fB->setComP ((BDRcomP) fcmp);
  960.        Binder::RegisterComP ((BDRcomP) fcmp);
  961.  
  962.        fB->save(ffile);
  963.  
  964.        delete fB;
  965.  
  966.        fbdr fb(ffile);
  967.  
  968.        fb.sort();
  969.  
  970.        fb.setFlags(BDR_DASSIGN);
  971.  
  972.        while (fb.prevAsg(&f))
  973.             cout << f << endl;
  974.  
  975.        return 0;
  976.      }
  977.  
  978.  
  979. The first list is dynamically allocated and limited to five nodes
  980. by its constructor.  InsNew() inserts after the current node making
  981. the newly inserted node current.  In the first iteration the
  982. current node is undefined so the new node is simply placed at the
  983. end of the list and made the current node.  The Binder compare
  984. function is set to the float compare but not used except to be
  985. stored on stream.  Compare functions must always be registered in
  986. order to be persistent.  (Upon registration, they are assigned an
  987. id which is what actually gets streamed.  Thus compare functions
  988. must always be registered in the same order to receive the same
  989. id.)  After the Binder is reloaded the BDR_DASSIGN flag is set so
  990. that primitives with "Asg" suffixes are enabled.  This second list
  991. is walked backwards after being sorted in descending order in order
  992. to count to five.
  993. A Binder stores itself on a stream by invoking Dstore(), a
  994. protected virtual function, for each of its elements.  Each Binder
  995. element is passed as the second parameter to Dstore().  Dstore()
  996. can only be invoked when the BDR_DSTORE flag is set.  The Binder's
  997. default Dstore() does nothing.  After all, a typeless Binder knows
  998. nothing about the data types of its elements.  But strongly type
  999. checked Binders do since template-form instances override Dstore().
  1000.  
  1001.  
  1002.      //template<class TYPE>
  1003.      //form's FTYPE replaces TYPE
  1004.  
  1005.      virtual  void Dstore(ostream& os, voiD D)
  1006.      {
  1007.           os << *(TYPE *)D << BDRendm;
  1008.      }
  1009.  
  1010.      virtual  voiD Dload(istream& is)
  1011.      {
  1012.           TYPE t;
  1013.  
  1014.           is >> t >> BDRnextm;
  1015.           return (voiD) new TYPE(t);
  1016.      }
  1017.  
  1018.  
  1019. The Binder's Dload() takes care of loading elements back from a
  1020. stream.  There is no BDR_DLOAD flag since loading from stream is a
  1021. constructor process.  To set a load flag in a call to a load
  1022. constructor is ludicrous.
  1023.  
  1024. In chapter 4, examp402.cpp, we saw how a string wrapper structure
  1025. was used to appease a Binder's template requirements.  Recall that
  1026. the stream insertion and extraction operators for this string
  1027. wrapper did nothing.  They were there so the Dstore() and Dload()
  1028. functions of the template-form instance had something to call. 
  1029. Since we didn't stream the Binder we didn't care.  Let's redo that
  1030. structure so that it can be streamed and we'll redo it with forms
  1031. this time just to be different.
  1032.      // examp502.cpp - link with binder.obj
  1033.      // rework of examp402.cpp
  1034.  
  1035.      #define sfile "examp502.txt"
  1036.      #include <string.h>
  1037.      #include <iostream.h>
  1038.      #include "binder.hpp"
  1039.  
  1040.      struct str {
  1041.        char *s;
  1042.        str(const char *cs = (char *)0)  // default constructor
  1043.        { s = (cs? strdup(cs) : (char *)0); }
  1044.        str(str& si)                     // copy initializer
  1045.          { s = (si.s? strdup(si.s) : si.s); }
  1046.        void operator=(str& si)          // assignment
  1047.          { delete s; s = (si.s? strdup(si.s) : si.s); }
  1048.        ~str() { delete s; }             // destructor
  1049.      };
  1050.  
  1051.      ostream& operator<<(ostream& os, str& si)
  1052.      {
  1053.        int i = (si.s? strlen(si.s) : 0);
  1054.  
  1055.        os << i << BDRendm;
  1056.        if (i)  os.write(si.s,i);
  1057.        return os;
  1058.      }
  1059.  
  1060.      istream& operator>>(istream& is, str& si)
  1061.      {
  1062.        char * D;
  1063.        int i;
  1064.  
  1065.        is >> i >> BDRnextm;
  1066.        if ((D = new char[i+i]) != (char *)0)  {
  1067.             if (i)
  1068.               is.read(D,i);
  1069.             D[i] = '\0';
  1070.             si.s = D;
  1071.        }
  1072.        return is;
  1073.      }      #define  FType    str
  1074.      #define  FBinder  StrBdr
  1075.      #define  FBindeR  StrBdR
  1076.  
  1077.      #include "fbinder.hpp"
  1078.  
  1079.      main()
  1080.      {
  1081.        StrBdr sb(BDR_DDELETE | BDR_DNEW | BDR_DSTORE);
  1082.  
  1083.        sb.ins(new str("line one"));
  1084.        str s("line two");
  1085.        sb.insNew(&s);
  1086.        sb.save(sfile);
  1087.  
  1088.        StrBdR sB = new StrBdr(sfile);
  1089.        if (!sB)  return 1;
  1090.        sB->setCurNode();      // reset current node
  1091.        while (sB->next())  cout << ((str *)*sB)->s << "\n";
  1092.        delete sB;
  1093.        return 0;
  1094.      }
  1095.  
  1096.  
  1097. The stream insertion operator writes the out the string's length
  1098. followed by the string itself.  Notice the use of the Binder's
  1099. BDRendm stream manipulator.  This manipulator inserts the Binder's
  1100. static char member, memberTermChar, between fields on a stream.  By
  1101. using os.write(), the string may contain the termination character. 
  1102. The stream extraction operator first reads the length, extracts the
  1103. terminator with the BDRnextm manipulator and then reads in the
  1104. string with is.read().  The reason the current node must be reset
  1105. when the string Binder is reloaded is because the state of the
  1106. current node is also persistent!
  1107.  
  1108. Also in the last chapter, in examp403.cpp, we created a Binder for
  1109. C++ strings in strbdr.cpp.  Recall that strbdr.hpp and strbdr.cpp
  1110. were cloned from fbinder.hpp and fbinder.cpp, in essence using
  1111. forms as a "cookbook."  This allowed us to use strings directly
  1112. with a strongly type checked Binder without resorting to a wrapper
  1113. class.  Let's continue with strbdr.cpp and see how Dstore() and
  1114. Dload() were overridden.  Instead of invoking an overloaded stream
  1115. insertion for our data type as is the case with a template or form
  1116. "template" instance, Dstore() is defined to do the insertion
  1117. directly. void StrBdr::Dstore(ostream& os, voiD D)
  1118.      {
  1119.           int i = strlen((char *)D);
  1120.      
  1121.           os << i << BDRendm;
  1122.           if (i)
  1123.                os.write((char *)D,i);
  1124.      }
  1125.  
  1126.  
  1127. Likewise the Dload() function is defined to extract the string
  1128. directly from the stream.
  1129.  
  1130.  
  1131.      voiD StrBdr::Dload(istream& is)
  1132.      {
  1133.           char * D;
  1134.           int i;
  1135.  
  1136.           is >> i >> BDRnextm;
  1137.           if ((D = new char[i+i]) != (char *)0)  {
  1138.                if (i)
  1139.                     is.read(D,i);
  1140.                D[i] = '\0';
  1141.           }
  1142.           return (voiD) D;
  1143.      }
  1144.  
  1145.  
  1146. The previous example then becomes:
  1147.  
  1148.  
  1149.      // examp503.cpp - link with strbdr.obj and binder.obj
  1150.      // rework of examp502.cpp
  1151.  
  1152.      #define sfile "examp503.txt"
  1153.  
  1154.      #include <string.h>
  1155.      #include "strbdr.hpp"
  1156.      main()
  1157.      {
  1158.        StrBdr sb(BDR_DDELETE | BDR_DNEW | BDR_DSTORE);
  1159.  
  1160.        sb.ins(strdup("line one"));
  1161.        sb.insNew("line two");
  1162.        sb.save(sfile);
  1163.  
  1164.        StrBdR sB = new StrBdr(sfile);
  1165.        if (!sB)  return 1;
  1166.        sB->setCurNode();      // reset current node
  1167.        while (sB->next())  cout << (char *)*sB << "\n";
  1168.        delete sB;
  1169.        return 0;
  1170.      }
  1171.  
  1172.  
  1173. Instead of using Binder::save() or the Binder load constructor, you
  1174. can insert or extract Binders directly to a stream.  The last
  1175. example then becomes:
  1176.  
  1177.  
  1178.      // examp504.cpp - link with strbdr.obj and binder.obj
  1179.      // rework of examp503.cpp
  1180.  
  1181.      #define sfile "examp504.txt"
  1182.  
  1183.      #include <string.h>
  1184.      #include <fstream.h>
  1185.      #include "strbdr.hpp"
  1186.  
  1187.      main()
  1188.      {
  1189.        StrBdr sb(BDR_DDELETE | BDR_DNEW | BDR_DSTORE);
  1190.  
  1191.        sb.ins(strdup("line one"));
  1192.        sb.insNew("line two");
  1193.        ofstream os(sfile);
  1194.        if (!os)  return 1;
  1195.        os << sb;
  1196.        os.close();        StrBdR sB;
  1197.        ifstream is(sfile);
  1198.        if (!is) return 1;
  1199.        is >> sB;
  1200.        is.close();
  1201.        if (!sB)  return 1;
  1202.        sB->setCurNode();      // reset current node
  1203.        while (sB->next())  cout << (char *)*sB << "\n";
  1204.        delete sB;
  1205.        return 0;
  1206.      }
  1207.  
  1208.  
  1209. Can you now see why the (char *) implicit type cast was used on
  1210. *sB?  That's right, so that the sB Binder itself wasn't streamed
  1211. out to cout.
  1212.  
  1213. You could of course add stream error logging to both Dstore() and
  1214. Dload() by calling the Binder's built-in error function defined as
  1215. follows.
  1216.  
  1217.  
  1218.      void Binder::berror(const char * msg)
  1219.      {
  1220.           if (streamDebug)
  1221.                cerr << endl << msg << endl;
  1222.      }
  1223.  
  1224.  
  1225. StreamDebug is a static public member of Binder defined as follows.
  1226.  
  1227.  
  1228.      int  Binder::streamDebug = 0;
  1229.  
  1230.  
  1231. Thus Binder stream error logging can be turned on by setting
  1232. streamDebug to a non zero value. Summary
  1233.  
  1234. Only strongly type checked Binders can be persistent.  A Binder's
  1235. complete state, i.e. compare function pointer, current node
  1236. setting, flags, etc., persists when stored on a stream.  Binder
  1237. compare functions must be registered in a consistent order to be
  1238. persistent since their id's are assigned in sequence according to
  1239. the order of their registration.  A Binder having an unregistered
  1240. compare function when reloaded from a stream will have a null
  1241. compare function pointer, the same as a newly constructed Binder. 
  1242. Binders can be saved on a stream by using either the save() member
  1243. function with a file name or the stream insertion operator with a
  1244. file handle.  Likewise, a Binder can be reload from a stream by
  1245. using either the load constructor or the stream extraction
  1246. operator.  The stream extraction operation extracts to a Binder
  1247. pointer.
  1248.                           Chapter 6
  1249.  
  1250.                Mutually Owned Containers and Nodes
  1251.  
  1252.  
  1253. Thus far we have only looked at simple Binders with simple
  1254. persistence.  MBinders are strongly type checked Binders that will
  1255. only bind objects derived from the Mutual class.  Such nodes may be
  1256. mutually owned, i.e. belong to more than one container at the same
  1257. time.  Since MBinders are themselves derived from Mutual, they too
  1258. can be multiply contained in other MBinders, etc.  A network of
  1259. MBinders and any assortment of nodes derived from Mutual can thus
  1260. be stored on a stream and later reloaded while the multiple
  1261. references to the various elements are automatically resolved! 
  1262. Function pointers can also be streamed if they are first
  1263. registered.  Using MBinders and Mutual nodes, you can quickly build
  1264. your own highly efficient application framework tool, graph theory
  1265. oriented case tool, data base schema representation or whatever
  1266. else without worrying about a single link!
  1267.  
  1268. To demonstrate, let's use a MBinder to warehouse int's and strings. 
  1269. Like typeless Binders, MBinders can hold heterogeneous data if it's
  1270. derived from Mutual.  First we must create a class derived from
  1271. Mutual for int's.  To do this we copy fmutual.hpp to mint.hpp and
  1272. fmutual.cpp to mint.cpp, similar to the way we cookbooked form
  1273. Binders for C++ strings.  Templates can't be used here because we
  1274. want to be able to derive a polymorphic cluster of classes from a
  1275. Mutual root, not just simply parameterize Mutual.
  1276.  
  1277. We proceed by following the files' commented directions globally
  1278. replacing CLASS with Mint, CPTR with MinT, and BASE with Mutual in
  1279. both files.  We also have to define the ID_Mint macro in mint.hpp
  1280. as a unique unsigned integer.  The initData() member function has
  1281. its only parameter filled in and the first public Mint()
  1282. constructor has this parameter filled in also.  An I() function has
  1283. been added to return Mint's private integer value.  After these
  1284. modifications we're left with mint.hpp as follows.
  1285.     #ifndef Mint_HPP
  1286.      #define Mint_HPP
  1287.  
  1288.      #ifndef Mutual_HPP
  1289.      #include "Mutual.hpp"
  1290.      #endif
  1291.  
  1292.      #define ID_Mint  2
  1293.      typedef class Mint * MinT;
  1294.      #define MinT0  ((MinT)0)
  1295.  
  1296.      class Mint : public Mutual  {
  1297.           // Mutual must be public for type conversion
  1298.           // to Mutual for polymorphic handling!
  1299.      private:
  1300.           /*  Mint-declared data members  */
  1301.           int i;
  1302.           int  initData(int i);
  1303.  
  1304.      protected:
  1305.  
  1306.           Mint     (initVFTs) : Mutual(initVFTsEtc) {}
  1307.           virtual  void fput(ostream& os);
  1308.           static   MutuaL fget(istream&, MutuaL InstancE);
  1309.  
  1310.      public:
  1311.  
  1312.           Mint     (int i);
  1313.           Mint     (Mint&);
  1314.           static   void RegisterClass()
  1315.                { Mutual::RegisterClass(ID_Mint,Mint::fget); }
  1316.           virtual  int operator=(Mutual&);
  1317.           virtual  MutuaL clone();
  1318.           virtual  unsigned ID()  { return ID_Mint; }
  1319.           virtual  ~Mint();
  1320.           int      I()  { return i; }
  1321.      };
  1322.  
  1323. #endif  /*  Mint_HPP  */ By using the form method we are saved the trouble of having to know
  1324. which functions to overload, etc.  Never the less, let's look at
  1325. the Mint class in detail.  The only new data member of Mint is the
  1326. integer i.  The initData() function must perform the initialization
  1327. of i and returns zero if it fails.  All this may seem unnecessary
  1328. to you for the moment, but an elaborate systematic method has been
  1329. worked out in advance for you that allows further extensibility of
  1330. the Mint class where each tier in the hierarchy is responsible for
  1331. its own data members during stream storing and loading operations
  1332. as OOP rightly dictates.  (In a class derived from Mint, BASE would
  1333. be replaced with Mint instead of Mutual!)  The protected Mint()
  1334. default constructor simply initializes its virtual function table
  1335. and any data that has to be minimally constructed and then calls up
  1336. the hierarchy chain to do likewise.  The reason this constructor
  1337. exists is so that objects derived from Mint can be reconstructed
  1338. from a stream.  Consider an object derived from Mint being reloaded
  1339. from a stream.  The object's static fget() protected function gets
  1340. called by the stream registry to reload the object.  This load
  1341. function can't be a constructor since you can't take the address of
  1342. a constructor in C++.  This drawback is worked around by declaring
  1343. fget() as static which does have an address.  This fget() calls the
  1344. default constructor of the class which in turn calls Mint's default
  1345. constructor and so on.  Initialization data for Mint isn't
  1346. available at this time.  Next fget() has the option of reloading
  1347. its data members from the stream or calling Mint::fget() first to
  1348. load its data members.  The order depends on which order the
  1349. object's fput() wrote the data out to the stream.  What ever the
  1350. order decided upon, the object's fget() will get around to calling
  1351. its initData() to do the initialization after extracting the
  1352. necessary data from the stream just as Mint::fget() will get around
  1353. to calling Mint::initData() after extracting its data.  The whole
  1354. process was designed to separate initialization from construction
  1355. thus allowing derived classes the ability to construct the whole
  1356. hierarchy while leaving the responsibility of the loading and
  1357. subsequent initialization to each tier for its respective data
  1358. members.  This approach has the added benefit of allowing tier data
  1359. to be ordered in various arrangements on the stream.  Furthermore,
  1360. by forcing an object's normal, non stream construction and its
  1361. initialization from stream to both be processed through the same
  1362. initData() function we guarantee consistency between the two
  1363. approaches.  The RegisterClass() function allows us to register the
  1364. class for streaming.  The order of class registration doesn't
  1365. matter since id's are user defined and not automatically supplied
  1366. by the registry.  The ID() function returns the object's class id.
  1367. The rest of the functions are for the benefit of MBinder, namely
  1368. operator=() for use with the Binder's Dassign() function, the copy
  1369. initializer constructor called in turn by clone() for use with the
  1370. Binder's Dnew() function, and of course the destructor for use with
  1371. the Binder's Ddelete() function or by itself.
  1372.  
  1373. Next we turn to the class derived from Mutual for strings (mstr.hpp
  1374. and mstr.cpp).  The mstr.cpp file requires a little more editing
  1375. than the mint.cpp file since strings don't load themselves from
  1376. streams the way we want them to.  Let's examine mstr.cpp.  One
  1377. function at a time will be listed interspersed with comments.
  1378.  
  1379.      #ifndef Mstr_HPP
  1380.      #include "mstr.hpp"
  1381.      #endif
  1382.  
  1383.      #include <string.h>
  1384.  
  1385.      int Mstr::initData(char *s, int dup)
  1386.      {
  1387.           this->s = ((dup && s)? strdup(s) : s);
  1388.           if (this->s)  return 1;  // success
  1389.           return 0;
  1390.      }
  1391.  
  1392. Both the string and int parameters have been added to initData(). 
  1393. The initData() function duplicates the string if requested by
  1394. "dup."
  1395.  
  1396.      void Mstr::fput(ostream& os)
  1397.      {
  1398.           Mutual::fput(os);
  1399.           int i = strlen(s);
  1400.      
  1401.           os << i << Mendm;
  1402.           if (i && os)  os.write(s,i);
  1403.           if (!os)
  1404.                error("unable to store Mstr "
  1405.                     "data on stream");
  1406.      } Like StrBdr::Dstore() in strbdr.cpp and examp503.cpp, fput() first
  1407. outputs the string length followed by the string.  Recall that the
  1408. Binder had a stream error function.  Well Mutual has a stream error
  1409. function also and this time we're using it.
  1410.  
  1411. The fget() function is the hardest, but remember most of the work
  1412. has been done for you when you clone fmutual.cpp.  Recall from our
  1413. discussion of Mint::fget() that an fget() function has to operate
  1414. in two modes.  If the InstancE pointer is NULL that means that the
  1415. fget() function is being called by the stream registry as the
  1416. constructor of the object being loaded from the stream.  If it is
  1417. not NULL than it is being called from a derived class' fget() to
  1418. initialize the Mstr's tier data and not as a constructor!  (We must
  1419. code fget() in fashion that allows Mstr to be extended!)
  1420.  
  1421.      MutuaL Mstr::fget(istream& is, MutuaL InstancE)
  1422.      {
  1423.           int newed;
  1424.           MstR thiS;
  1425.           char * s;
  1426.           int i;
  1427.           is >> i >> Mnextm;
  1428.           if (!is)  {
  1429.                serror("unable to load Mstr "
  1430.                     "data from stream",
  1431.                     ID_Mstr);
  1432.                return MutuaL0;
  1433.           }
  1434.           if ((s = new char[i+i]) != (char *)0)  {
  1435.                if (i)  {
  1436.                     is.read(s,i);
  1437.                     if (!is)  {
  1438.                          delete s;
  1439.                          return MutuaL0;
  1440.                     }
  1441.                }
  1442.                s[i] = '\0';
  1443.           }
  1444.           if (InstancE)  {
  1445.                newed = 0;
  1446.                thiS = (MstR) InstancE;
  1447.           } else  {
  1448.                if ((thiS = new Mstr(initVFTsEtc))
  1449.                     == MstR0)  {
  1450.                     serror("unable to construct "
  1451.                          "new Mstr for "
  1452.                          "loading",
  1453.                          ID_Mstr);
  1454.                     return MutuaL0;
  1455.                }
  1456.                newed = 1;
  1457.                InstancE = (MutuaL) thiS;
  1458.           }
  1459.           if (!Mutual::fget(is,InstancE))  {
  1460.                if (newed)
  1461.                     delete (voiD) thiS;
  1462.                return MutuaL0;
  1463.           }
  1464.           if (!thiS->initData(s,0))  {
  1465.                serror("unable to initialize Mstr "
  1466.                     "from reloaded stream data",
  1467.                     ID_Mstr);
  1468.                if (newed)
  1469.                     delete (voiD) thiS;
  1470.                return MutuaL0;
  1471.           }
  1472.           return InstancE;
  1473.      }
  1474.  
  1475. The string's length is read first from the stream followed by the
  1476. string itself.  Then InstancE is checked to see if this fget() is
  1477. being called as a constructor or as a tier initializer.  After this
  1478. has been decided and taken care of, the base class fget() is called
  1479. to take care of its tier data.  In our case, Mutual::fget() doesn't
  1480. do anything but it does provide the coding style for calling any
  1481. base class fget().  Notice that our arrangement has the base class
  1482. data being stored on the stream after the derived class data.  If
  1483. you need it the other way around move all the string's extraction
  1484. code up to but not including the "if (InstancE)" line to
  1485. immediately after the "if (!Mutual::fget(is,InstancE))" block. 
  1486. Your fput() function would also have to call base::fput(os) before
  1487. inserting its tier data in the stream.  Notice that because a
  1488. buffer was allocated for the string here, initData() is called with
  1489. the duplication option turned off.  This is why initData() had the
  1490. option! Mstr::Mstr (char *s)
  1491.           : Mutual(initVFTsEtc)
  1492.      { (void) initData(s); }
  1493.  
  1494. A string parameter is added to the constructor just like an int
  1495. parameter was added in mint.hpp.  InitData()'s dup parameter
  1496. defaults to "on."
  1497.  
  1498.      Mstr::Mstr(Mstr& c) : Mutual(c)
  1499.      {
  1500.           // copy initializer constructor
  1501.           if (c.S())
  1502.                s = strdup(c.S());
  1503.           else
  1504.                s = (char *) 0;     
  1505.      }
  1506.  
  1507. The copy initializer constructor is defined to duplicate the string
  1508. of the original Mstr if there is one.  Mstr::S() returns its string
  1509. pointer which always points to a dynamically allocated string!
  1510.  
  1511.      int Mstr::operator=(Mutual& m)
  1512.      {
  1513.           delete s;
  1514.           s = (char *) 0;
  1515.           if ((s = ((MstR)&m)->S()) != (char *)0)
  1516.                s = strdup(s);
  1517.           return 1;  // enabled
  1518.      }
  1519.  
  1520. Remember, assignment differs from initialization in that the old
  1521. string must be properly disposed of, i.e. deleted.  The assignment
  1522. operator is an overloaded virtual function originating in the
  1523. Mutual class, hence the Mutual reference parameter.
  1524.  
  1525.      MutuaL Mstr::clone()
  1526.      {
  1527.           // invokes copy initializer constructor
  1528.           return (MutuaL) new Mstr(*this);
  1529.      }
  1530.  
  1531. Nothing is modified in clone(). Mstr::~Mstr()
  1532.      {
  1533.           delete s;
  1534.      }
  1535.  
  1536. The destructor must of course delete the string.
  1537.  
  1538. This seems like a lot of work to make something mutually ownable,
  1539. persistent, and extensible.  But you must remember that C++ has yet
  1540. to address persistence in its language definition.  If you use
  1541. fmutual.hpp and fmutual.cpp to cookbook your own it will go
  1542. smoothly.  At last we're ready to look at our example.
  1543.  
  1544.  
  1545.      // examp601.cpp - link with binder.obj, mutual.obj,
  1546.      // mint.obj, and mstr.obj
  1547.      
  1548.      #define bfile  "examp601.txt"
  1549.  
  1550.      #include <string.h>
  1551.      #include "mint.hpp"
  1552.      #include "mstr.hpp"
  1553.  
  1554.      // display integers and strings
  1555.  
  1556.      void display(MutuaL M)
  1557.      {
  1558.           switch (M->ID())  {
  1559.           case ID_Mint:
  1560.                cout << ((MinT)M)->I() << endl;
  1561.                break;
  1562.           case ID_Mstr:
  1563.                cout << ((MstR)M)->S() << endl;
  1564.                break;
  1565.           default:
  1566.                cout << "unknown" << endl;
  1567.                break;
  1568.           }
  1569.      } // sort integers and strings
  1570.  
  1571.      int mintstrcmp(MutuaL M1, MutuaL M2)
  1572.      {
  1573.           // integers sorted to the front
  1574.           // strings sorted to the rear
  1575.  
  1576.           if (M1->ID() == ID_Mint)
  1577.                if (M2->ID() == ID_Mint)
  1578.                     return ((MinT)M1)->I()
  1579.                     - ((MinT)M2)->I();
  1580.                else
  1581.                     return -1;
  1582.           else
  1583.                if (M2->ID() == ID_Mint)
  1584.                     return 1;
  1585.                else
  1586.                     return strcmp(
  1587.                          ((MstR)M1)->S(),
  1588.                          ((MstR)M2)->S());
  1589.      }
  1590.  
  1591.      main()
  1592.      {
  1593.           Mint::RegisterClass();
  1594.           Mstr::RegisterClass();
  1595.  
  1596.           MBinder b1;
  1597.  
  1598.           b1.push(new Mstr("Hello LDB!"));
  1599.           b1.insQ(new Mstr("Goodbye linked"));
  1600.           b1.insQ(new Mstr("list programming!"));
  1601.           b1.insQ(b1.rear());
  1602.           b1.insQ(new Mstr(
  1603.                "Line above tests multilinking!"));
  1604.  
  1605.           for (int i = 3; i; i--)
  1606.                b1.insQ(new Mint(i));
  1607.  
  1608.          cout << "\n\nMBinder of streamable integers"
  1609.                << " and strings!\n\n";
  1610.  
  1611.           b1.forEach((BDRapplY)display);
  1612.  
  1613.           cout << "\n\nPress enter to continue ...";
  1614.           cin.get();
  1615.  
  1616.           b1.setComP((BDRcomP)mintstrcmp);
  1617.  
  1618.           Binder::RegisterComP((BDRcomP)mintstrcmp);
  1619.  
  1620.           (void) b1.save(bfile);
  1621.  
  1622.           b1.allDel();
  1623.  
  1624.           MBinder b2(bfile);
  1625.  
  1626.           cout << "\nStreamed and "
  1627.                << "reloaded MBinder with "
  1628.                << "multiple links maintained "
  1629.                << "\nand sorted with streamed "
  1630.                << "compare fnc, ints in front:"
  1631.                << " \n";
  1632.  
  1633.           Restream();
  1634.  
  1635.               // Don't load again from
  1636.               // any stream without
  1637.               // restreaming StreamRegistry!!!
  1638.  
  1639.           b2.sort();
  1640.  
  1641.           b2.forEach((BDRapplY)display);
  1642.  
  1643.           return 0;
  1644.      }
  1645. Notice how the display function takes the Mutual pointer and reads
  1646. the ID() to determine if we have a mutually owned int or string,
  1647. i.e. Mint or Mstr.  The compare function works the same way making
  1648. int's less than strings, thus forcing int's ahead of strings when
  1649. sorting.  Once we get into main(), both the Mint and Mstr classes
  1650. are registered with the stream registry.  Then several strings are
  1651. created and queued.  The string "list programming!" is requeued for
  1652. a second time to show the same Mstr instance being bound twice in
  1653. the same Binder.  Don't worry, it won't be stored or reloaded from
  1654. stream twice.  Nor will it be deleted twice.  Next, some integers
  1655. are queued and the forEach iterator applies the display function to
  1656. each node of the Binder.  Then the Binder is saved on stream before
  1657. deleting it.  The compare function is of course registered before
  1658. streaming.  The Binder is then reloaded from stream, sorted and
  1659. displayed.
  1660.  
  1661. The Restream() function clears the stream registry's holding pen of
  1662. any remaining left over multilink instances.  If any are remaining
  1663. and the static StreamRegistry::debug is turned on (non zero), these
  1664. left over errors are reported on cerr.
  1665.  
  1666. The Mutual class also has two static switches, refDebug and
  1667. streamDebug.  When refDebug is turned on any over/under linking
  1668. attempts to a Mutual object is reported on cerr.  When streamDebug
  1669. is turn on the Mutual stream error and warn functions report on
  1670. cerr also.  Mutual objects also inherit a restream() function which
  1671. must be called between streaming sessions.  In our example, we
  1672. didn't need to call b1.restream() since we disposed of b1 without
  1673. attempting to save it again on a stream.  But if we had called
  1674. b1.restream() with streamDebug turned on, any node that had more
  1675. reference links than storage attempts would have complained via
  1676. Mutual::error() reporting "restream: streamed less than
  1677. referenced".  For more details see the reference chapter.
  1678.  
  1679. A Mutual object can't be stored via the stream registry if it
  1680. hasn't been linked to.  Each time a node is bound in a MBinder its
  1681. link count is incremented.  Each time a node is released from a
  1682. MBinder its link count is decremented.  Remember, Mutual::refDebug
  1683. controls the reporting of over/under linking.  How was it then that
  1684. b1, a MBinder derived from Mutual, in our example could be streamed
  1685. when it wasn't linked to anything nor registered?  The answer is
  1686. that it wasn't stored via the stream registry, but rather via its
  1687. inherited Binder save() function.  Actually a Binder is stored on
  1688. the stream instead of an MBinder.  That's okay, because its nodes
  1689. are processed by the stream registry anyway.  Any MBinders queued  into b1 would have been stored via the stream registry as MBinders
  1690. as expected.  The MBinder class would have to be registered of
  1691. course.  If we used the stream insertion and extraction operators
  1692. instead of the save() and load constructor, the stream registry
  1693. would be called into play instead of the inherited Binder streaming
  1694. functions.  You can not mix the two methods since the Binder
  1695. functions stream a Binder and the stream registry streams an
  1696. MBinder, i.e. the Mutual id and multilink data.  The stream
  1697. registry approach is used in examp602.cpp, an excerpt of which
  1698. follows.
  1699.  
  1700.  
  1701.           MBinder::RegisterClass();
  1702.  
  1703.           ...
  1704.  
  1705.           ofstream os(bfile);
  1706.           if (!os)  {
  1707.                b1.allDel();
  1708.                return 1;
  1709.           }
  1710.           b1.link();
  1711.           os << b1;
  1712.           os.close();
  1713.  
  1714.           b1.allDel();
  1715.  
  1716.           ifstream is(bfile);
  1717.           if (!is)
  1718.                return 1;
  1719.           MutuaL M;
  1720.           is >> M;
  1721.           is.close();
  1722.           if (!M)
  1723.                return 1;
  1724.           if (M->ID() != ID_MBinder)
  1725.                return 1;
  1726.           MBindeR B2 = (MBindeR) M;
  1727.  
  1728.           ...
  1729.  
  1730.           delete B2; The MBinder class is now registered.  Notice that b1 had to be
  1731. linked to before storing otherwise the stream registry would have
  1732. reported "more stores attempted than links."  Only Mutual objects
  1733. may be extracted from a Mutual object stream.  It is not known
  1734. ahead of time whether or not the Mutual object happens to be an
  1735. MBinder.  The id is checked to verify that we have loaded an
  1736. MBinder.  Only then is the Mutual pointer assigned to the MBinder
  1737. pointer B2, reflecting the fact that reloaded objects are
  1738. dynamically allocated and reminding us that B2 must be deleted
  1739. before exiting main().
  1740.      
  1741. Well then, how does multilink resolution actually work?  When an
  1742. instance of a class derived from Mutual, let's call it X, is
  1743. referenced by more than one object, all of which are also being
  1744. stored onto a stream, the first object that attempts to store X
  1745. will succeed while the rest will automatically store only a link
  1746. reference to X.  When reloading X, X will be automatically held in
  1747. a holding pen inside of the stream registry if it is multiply
  1748. referenced.  When the additional objects referencing X are
  1749. reloaded, the links to X are recovered.  When the last reference to
  1750. X is loaded and that last link to X is reconstructed, X is released
  1751. automatically from the holding pen.  This mechanism prevents
  1752. multiple copies of X from being stored on the stream and thus
  1753. multiple copies being reloaded.  The holding pen (a Binder "data
  1754. base" of multiple references) is self cleaning and thus requires no
  1755. attention from the programmer.  Restream() double checks to see if
  1756. the holding pen is clean, complaining if it has to clean up left
  1757. overs.  Mutual::restream() complains if X has more links then
  1758. attempts at storing since the last restream() operation.  The
  1759. MBinder::restream() function automatically calls each of its node's
  1760. restream() function.
  1761.                             Summary
  1762.  
  1763.  
  1764. Derive all your mutually owned, persistent objects from Mutual by
  1765. cloning fmutual.hpp and fmutual.cpp (following the commented
  1766. directions).  Use MBinder to bind Mutual objects.  Since MBinder is
  1767. derived from Mutual itself, it can be readily bound into one or
  1768. more MBinders.  It's also permissible to use Mutual objects without
  1769. MBinders (see reference chapter).  You must register each class
  1770. that will be streamed, however.  The order of class registration is
  1771. not important since id's are user defined.  Base classes, that have
  1772. no instances of themselves streamed, do not have to be registered. 
  1773. The act of registration is simply the recording of a class' id with
  1774. its associated loader function, i.e. fget().  The intermediate, non
  1775. instantiated base class fget()'s are not called from the stream
  1776. registry but rather from the fget() of the object being loaded. 
  1777. See the StreamRegistry section in reference chapter for details on
  1778. streaming function pointers.  All streamed data pointers must point
  1779. to registered Mutual objects.  Be sure to call Restream() to reset
  1780. the stream registry between each streaming session.  You must also
  1781. call Mutual::restream() between sessions for each object streamed. 
  1782. MBinder::restream() automatically calls each node's restream()
  1783. function.  You can set Mutual::streamDebug to a non zero value to
  1784. have streaming errors reported on cerr.  Likewise you can set
  1785. Mutual::refDebug to report over/under linking attempts to a Mutual
  1786. object.  The StreamRegistry::debug switch enables the reporting of
  1787. holding pen errors, etc.
  1788.                            Chapter 7
  1789.  
  1790.                             Reference
  1791.  
  1792.  
  1793. -----------------------------------------------------------------
  1794. Class: Binder                                          binder.hpp
  1795. -----------------------------------------------------------------
  1796.  
  1797.  
  1798. Inherited by: MBinder
  1799.  
  1800. A Binder is an elastic array of void pointers having the added
  1801. behaviors of stack, queue, deque, list, etc.  To achieve strong
  1802. type checking use templates or forms.  Only strongly type checked
  1803. Binders can be persistent.
  1804.  
  1805. Template usage:
  1806.  
  1807.           #include "tbinder.hpp"
  1808.           TBINDER(TYPE, TYPEBINDER, TYPEBINDERPTR);
  1809.  
  1810.      where
  1811.  
  1812.           TYPE is the data type to check for,
  1813.           TYPEBINDER is the name of this new Binder,
  1814.           and TYPEBINDERPTR is a pointer to the new Binder.
  1815.  
  1816.      For example, to create a strongly type checked Binder for
  1817.      integers:
  1818.  
  1819.           #include "tbinder.hpp"
  1820.           TBINDER(int,IntBdr,IntBdR);
  1821.  
  1822.      To use the template TYPE must have:
  1823.  
  1824.           a default constructor,
  1825.           a copy initializer constructor,
  1826.                i.e. TYPE(TYPE&)
  1827.           a destructor,
  1828.           an assignment operator, i.e. operator=(),
  1829.           a stream insertion operator,
  1830.                i.e. ostream& operator<<(ostream&,TYPE&),
  1831.           and a stream extraction operator,
  1832.                i.e. istream& operator>>(istream&,TYPE&).
  1833. Class: Binder                                         binder.hpp
  1834. -----------------------------------------------------------------
  1835.  
  1836.  
  1837.      If your compiler doesn't support C++ templates or your data
  1838.      type doesn't meet these requirements use forms instead.
  1839.  
  1840. Form usage:
  1841.  
  1842.           #define   FType          int
  1843.           #define   FBinder        IntBdr
  1844.           #define   FBindeR        IntBdR
  1845.  
  1846.           #include  "fbinder.hpp"
  1847.  
  1848.      Fbinder.hpp leaves FType, FBinder, and FBindeR undefined thus
  1849.      allowing multiple form "templates" to be used in the same
  1850.      file.
  1851.  
  1852.      FType must meet the same requirements as the template's TYPE
  1853.      parameter.  If not, clone the fbinder.hpp and fbinder.cpp
  1854.      files and edit these according to the commented directions. 
  1855.      Be sure to read chapters 4 and 5.
  1856.  
  1857. Members, private:
  1858.  
  1859.           unsigned  lowLimit,  lowThreshold,  first;
  1860.           voiDV     linkS;
  1861.           unsigned  limit,     delta,    nodes;
  1862.           unsigned  maxNodes,  curNode,  flags;
  1863.           BDRcomP   comP;
  1864.  
  1865.      The value of "limit" is the current size of the array pointed
  1866.      to by "linkS".  The value of "delta" is how much the array
  1867.      will grow upon overflow.  "Nodes" is the number of cells in
  1868.      the array that are currently occupied.  "MaxNodes" is the
  1869.      upper bound for the growth of "limit".  "LowTheshold" is the
  1870.      number of logical cells the Binder has to shrink to before
  1871.      shrinking the physical size of the array pointed to by
  1872.      "linkS".  When compacted the array will be shrunk to the new
  1873.      limit of "lowLimit".  "First" maps the Binder's logical cell
  1874.      zero into a starting cell position in the physical array
  1875.      pointed to by "linkS".  "CurNode" is the current node
  1876.      associated with the Binder's list behavior.
  1877. Class: Binder                                         binder.hpp
  1878. -----------------------------------------------------------------
  1879.  
  1880.  
  1881.      "Flags" has the following bit definitions.
  1882.  
  1883.           #define BDR_SORTED       0x01U
  1884.           #define BDR_BIND_ONLY    0x00U
  1885.           #define BDR_DASSIGN      0x02U
  1886.           #define BDR_DNEW         0x04U
  1887.           #define BDR_DDELETE      0x08U
  1888.           #define BDR_DSTORE       0x10U
  1889.  
  1890.      The BDR_SORTED flag indicates whether the Binder is still
  1891.      sorted since the last sort operation.  BDR_DASSIGN enables
  1892.      primitives ending with the "Asg" suffix.  BDR_DNEW enables the
  1893.      primitives ending with the "New" suffix.  BDR_DDELETE enables
  1894.      the primitives with "Del" or "del" in their names.  It also
  1895.      causes the destructor to call allDel() instead of allRmv(). 
  1896.      BDR_DSTORE enables the save() and overloaded stream insertion
  1897.      operators.  The comP variable holds the current compare
  1898.      function pointer.
  1899.  
  1900.           static   Binder&  comPv;
  1901.           static   unsigned comPID(BDRcomP comP);
  1902.           static   BDRcomP  comPLU(unsigned ID);
  1903.  
  1904.      The comPv is a vector of registered compare function pointers. 
  1905.      The id of a compare function is its index + 1.  ComPID()
  1906.      returns the id of a compare function pointer and comPLU()
  1907.      looks up an id returning its compare function pointer.  These
  1908.      members are used inside store() and load() to stream compare
  1909.      functions.
  1910.  
  1911.           int initData(unsigned flags,
  1912.                     unsigned maxNodes,
  1913.                     unsigned limit,
  1914.                     unsigned delta);
  1915.  
  1916.      The initData() initializer is called by the Binder
  1917.      constructors and the stream load() function guaranteeing
  1918.      consistency between the two approaches to Binder creation.
  1919. Class: Binder                                         binder.hpp
  1920. -----------------------------------------------------------------
  1921.  
  1922.  
  1923. Functions, protected:
  1924.  
  1925.      Binder  (initVFTs) {}
  1926.  
  1927.           This default constructor provides a mechanism for stream
  1928.           loader functions to construct an object's hierarchy
  1929.           without initializing it.  The parameter is a dummy to
  1930.           present a style for latter extensibility that will insure
  1931.           that an unambiguous default constructor is available.
  1932.  
  1933.      void  destruct();
  1934.  
  1935.           You must override the Binder destructor in any descendant
  1936.           that overrides Ddelete() or Ddetach(), i.e.
  1937.  
  1938.                virtual ~DerivedFromBinder()
  1939.                          { Binder::destruct(); }
  1940.  
  1941.           This is because the base destructors are called after
  1942.           virtual function tables are reset to their default values
  1943.           for the base level.  This means that Binder::~Binder()
  1944.           calls Binder::Ddetach() and perhaps Binder::Ddelete()
  1945.           instead of the intended DerivedFromBinder::Ddetach() or
  1946.           DerivedFromBinder::Ddelete().  By performing the actual
  1947.           destruction in the destruct() function the overridden
  1948.           destructor can be simply coded inline as shown.
  1949.  
  1950.      virtual voiD Dassign(voiD, const voiD)
  1951.                { return voiD0; }
  1952.  
  1953.           Called by atPutAsg(), atGetAsg(), popAsg(), etc. to copy
  1954.           data between a node and an external object.  Template and
  1955.           form instances override Dassign() redefining it to call
  1956.           the operator=() function for your data type.  It is
  1957.           guaranteed that Dassign() is never called unless the
  1958.           BDR_DASSIGN flag is set and the parameters are not NULL! 
  1959.           If Dassign() returns NULL the calling primitive
  1960.           gracefully fails leaving the Binder in an unaltered
  1961.           state.  MBinders use this function to call Mutual's
  1962.           operator=() function.
  1963. Class: Binder                                         binder.hpp
  1964. -----------------------------------------------------------------
  1965.  
  1966.  
  1967.      virtual voiD Dnew(const voiD)  { return voiD0; }
  1968.  
  1969.           Called by atInsNew(), pushNew(), atPutNew(), etc. to
  1970.           clone the data given as the parameter.  Template and form
  1971.           instances override Dnew() redefining it to call the copy
  1972.           initializer constructor for your data type.  It is
  1973.           guaranteed that Dnew() is never called unless the
  1974.           BDR_DNEW flag is set and the parameter is not NULL.  If
  1975.           Dnew() returns NULL the calling primitive gracefully
  1976.           fails leaving the Binder in an unaltered state.  MBinders
  1977.           use this function to call Mutual's clone() function.
  1978.  
  1979.      virtual void Ddelete(voiD D)  { delete (voiD) D; }
  1980.  
  1981.           Called by atDel(), allDel(), popDel(), del(), etc. to
  1982.           delete Binder nodes as they are removed from the Binder. 
  1983.           Template and form instances override Ddelete() redefining
  1984.           it to call the appropriate destructor for your data type. 
  1985.           It is guaranteed that Ddelete() is never called unless
  1986.           the BDR_DDELETE flag is set and the D parameter is not
  1987.           NULL.  MBinders carry out the deletion of Mutual nodes
  1988.           only when their refCount's are zero.
  1989.  
  1990.      virtual int  Dattach(voiD)  { return 1; }
  1991.  
  1992.           Called by any Binder primitive that attempts to bind
  1993.           data, e.g. atIns(), push(), etc..  This allows the data
  1994.           to become aware of the binding process by overriding
  1995.           Dattach().  The voiD parameter is a pointer to the data
  1996.           about to be bound and is guaranteed never to be NULL!  If
  1997.           and only if the data can't attach itself to the Binder
  1998.           should zero be returned.  Any overriding function should
  1999.           only link itself to the Binder and should not use the
  2000.           implicit "this" pointer to access the Binder within the
  2001.           overriding function.  This restriction applies because
  2002.           Binder data may be in a transition state when Dattach()
  2003.           is called.  It is however permissible to store the "this"
  2004.           pointer within the data being bound for later use in
  2005.           accessing Binder members.  Template and form instances do
  2006.           not override this function.  MBinders use this virtual
  2007.           function hook to link to their Mutual nodes.
  2008. Class: Binder                                         binder.hpp
  2009. -----------------------------------------------------------------
  2010.  
  2011.  
  2012.      virtual void Ddetach(voiD)  { return; }
  2013.  
  2014.           Ddetach() is called by any Binder primitive that attempts
  2015.           to unbind data, e.g. atDel(), pop(), etc..  The voiD
  2016.           parameter is never NULL!  Once called, the node must
  2017.           consider itself detached from the Binder!  Template and
  2018.           form instances do not override this function.  MBinders
  2019.           use this virtual function hook to unlink from their
  2020.           Mutual nodes.
  2021.  
  2022.      static  void sberror(const char * msg);
  2023.  
  2024.           If Binder::streamDebug is non zero the message parameter
  2025.           is streamed to cerr.  This Binder error function is
  2026.           static so that it can be called from the static load()
  2027.           function.
  2028.  
  2029.      virtual void berror(const char * msg);
  2030.  
  2031.           If Binder::streamDebug is non zero the message parameter
  2032.           is streamed to cerr.  This function is called from the
  2033.           store() function.
  2034.  
  2035.      virtual void Dstore(ostream&, voiD) {}
  2036.  
  2037.           Dstore() is called for each node after the Binder's
  2038.           header is stored on the stream.  The voiD parameter is
  2039.           guaranteed never to be NULL.  Template and form instances
  2040.           override this function redefining it to invoke the
  2041.           overloaded stream insertion operator for your data type. 
  2042.           MBinders use this function to invoke operator<<
  2043.           (ostream&, Mutual&) on Mutual nodes.
  2044. Class: Binder                                         binder.hpp
  2045. -----------------------------------------------------------------
  2046.  
  2047.  
  2048.      virtual voiD Dload(istream&) { return voiD0; }
  2049.  
  2050.           Dload() is called for each node on a stream after the
  2051.           Binder's header is loaded.  The Binder binds what Dload
  2052.           returns.  Template and form instances override this
  2053.           function redefining it to invoke the default constructor
  2054.           for a temporary variable of your data type, which it then
  2055.           invokes the overloaded stream extraction operator on. 
  2056.           The new operator is then applied to the copy initializer
  2057.           constructor for your data type using this temporary
  2058.           variable as its parameter.  The resultant pointer is
  2059.           returned.  MBinders use this function to invoke the
  2060.           operator>> (istream&, MutuaL&) function which calls the
  2061.           stream registry to extract the class id from the stream,
  2062.           lookup the load function, and load the instance.  Note
  2063.           that by extracting to a pointer (implicit allocation)
  2064.           that construction and initialization of a temporary
  2065.           variable is bypassed!
  2066.  
  2067.      virtual void store(ostream& os);
  2068.  
  2069.           Called by the Binder:: save() function, the stream
  2070.           insertion operators defined by the template and form
  2071.           instances, and the MBinder:: fput() function to store the
  2072.           Binder on the os stream.  Store() is never called unless
  2073.           the BDR_DSTORE flag is set.  The Binder's header is saved
  2074.           first followed by its nodes.  The nodes are saved via
  2075.           Dstore().  Template and form instances, and MBinders do
  2076.           not override this store() function.  Instead they
  2077.           override Dstore().  The purpose of overriding the store()
  2078.           function would be to store additional header information
  2079.           contained in a class derived from Binder.  The MBinder
  2080.           supplied stream insertion operator invokes the stream
  2081.           registry's fput() which stores id and multilink
  2082.           information ahead of the Binder information, then calls
  2083.           MBinder:: fput() which in turn calls this store function. 
  2084.           Since the stream registry's fput() takes care of the
  2085.           additional data inherited from Mutual, MBinder has no
  2086.           need to override this store() function.  Note that the
  2087.           MBinder:: save() function bypasses the stream registry
  2088.           effectively storing a Binder instead of an MBinder.
  2089. Class: Binder                                         binder.hpp
  2090. -----------------------------------------------------------------
  2091.  
  2092.  
  2093.      static  BindeR load(istream& is, BindeR thiS);
  2094.  
  2095.           The load() can be called by either the Binder's load
  2096.           constructor via vload() or a derived class' static load()
  2097.           function.  Load() extracts the necessary Binder
  2098.           initialization parameters from the stream and calls
  2099.           initData().  If "thiS" is NULL, load() allocates a new
  2100.           Binder using the protected default constructor,
  2101.           Binder(initVFTs), before calling initData().  The
  2102.           Binder's nodes are then extracted from the stream by
  2103.           repeatedly calling Dload().  The state of the current
  2104.           node, flags, and any compare function is also restored. 
  2105.           The BDR_DDELETE flag is raised if not set already since
  2106.           reloaded nodes are dynamic!  If load() fails to load the
  2107.           Binder from stream, it returns BindeR0, the NULL pointer. 
  2108.           Failure to load a Binder node does not constitute a
  2109.           failure to load the Binder.  Load() uses the sberror()
  2110.           function to report errors.
  2111.  
  2112.      int  vload(const char * filename, BDRsloaD sloaD, voiD thiS);
  2113.  
  2114.           The load constructor, i.e. Binder(const char * filename),
  2115.           calls vload() with the static load() function.  Vload()
  2116.           prepares the stream and calls load().  If anything fails
  2117.           vload() leaves the Binder in an acceptable, default
  2118.           constructed state.  Any class derived from Binder can
  2119.           define a load constructor by simply calling vload() with
  2120.           its static load() function.  This in effect "virtualizes"
  2121.           the load constructor.
  2122. Class: Binder                                         binder.hpp
  2123. -----------------------------------------------------------------
  2124.  
  2125.  
  2126. Miscellaneous, public:
  2127.  
  2128.      static  int   streamDebug;
  2129.  
  2130.           When non zero, streamDebug enables stream error reporting
  2131.           on cerr.  Used by sberror() and berror() functions.  Its
  2132.           default value is zero.
  2133.  
  2134.      static  char  memberTermChar;
  2135.  
  2136.           Used as the data member delimiter by the stream
  2137.           manipulators, BDRendm and BDRnextm.  The default value is
  2138.           '\n', the new line character.
  2139.  
  2140.      Though not members of the Binder proper, the following stream
  2141.      manipulators are used to insert/extract the memberTermChar.
  2142.  
  2143.           ostream& BDRendm(ostream& os);
  2144.           istream& BDRnextm(istream& is);
  2145.  
  2146.      static  void  RegisterComP(BDRcomP comP);
  2147.  
  2148.           Use this function to register Binder compare functions
  2149.           for streaming.  Since function id's are assigned
  2150.           sequentially, always register your compare functions in
  2151.           the same order to insure that id's read from stream are
  2152.           meaningful.  RegisterComP() uses the comPv Binder to
  2153.           record functions.
  2154.  
  2155.      static  void  ForgetComPs()  { comPv.allDel(); }
  2156.  
  2157.           After all Binder streaming is accomplished within your
  2158.           program it is permissible to forget their registrations
  2159.           to free up a little heap space.  ForgetComPs() deletes
  2160.           all entries recorded with RegisterComP().
  2161. Class: Binder                                         binder.hpp
  2162. -----------------------------------------------------------------
  2163.  
  2164.  
  2165. Constructor and Destructor primitives, public:
  2166.  
  2167.      Binder(unsigned flags = BDR_BIND_ONLY,
  2168.           unsigned maxNodes = BDR_MAXNODES,
  2169.           unsigned limit = BDR_LIMIT,
  2170.           unsigned delta = BDR_DELTA)
  2171.           { (void) initData(flags,maxNodes,limit,delta); }
  2172.  
  2173.           This constructor defaults to initializing a Binder so
  2174.           that node assigning, cloning, and deleting primitives are
  2175.           inhibited, e.g. atGetAsg(), atInsNew(), atDel(), etc.,
  2176.           see Binder:: flags.  Thus the destructor will call
  2177.           allRmv() instead of allDel().  MBinders set the flags
  2178.           parameter to the default value of (BDR_DDELETE |
  2179.           BDR_DSTORE), thus enabling deleting and storing
  2180.           primitives and forcing the destructor to call allDel()
  2181.           instead of allRmv().  Don't leave dynamically allocated
  2182.           nodes in Binder upon destruction unless the BDR_DDELETE
  2183.           is set or they will be left dangling!  Likewise don't
  2184.           bind statically allocated nodes in a Binder that has its
  2185.           BDR_DDELETE flag set or the destructor will try to
  2186.           deallocate them!  The Binder is limited to "maxNodes"
  2187.           starting off with an internal physical array big enough
  2188.           to hold "limit" nodes which will grow or shrink at
  2189.           "delta" rate.  BDR_LIMIT is defined as 20 and BDR_DELTA
  2190.           as 10 while BDR_MAXNODES is defined as ((unsigned)
  2191.           (UINT_MAX / sizeof(voiD))).  "VoiD" is typedef'ed as a
  2192.           pointer to void.
  2193.  
  2194.      Binder  (voiDV argv, unsigned argc = 0,
  2195.           unsigned flags = BDR_BIND_ONLY);
  2196.  
  2197.           This constructor takes "argv", a vector of void pointers,
  2198.           and builds a Binder by copying each void pointer into a
  2199.           cell of the Binder's array.  If "argc" is zero then
  2200.           "argv" is expected to be NULL terminated.  This
  2201.           constructor calls the first constructor with "flags" to
  2202.           do the actual Binder initialization then proceeds to loop
  2203.           through the vector queuing the void pointers.
  2204. Class: Binder                                         binder.hpp
  2205. -----------------------------------------------------------------
  2206.  
  2207.  
  2208.      Binder  (const char *filename)
  2209.           { (void) vload(filename,(BDRsloaD)Binder::
  2210.                load,this); }
  2211.  
  2212.           This constructor will load a Binder from a file.  If the
  2213.           operation fails the Binder will be left in an acceptable
  2214.           default constructed state, i.e. no nodes.  The state of
  2215.           the current node, flags, and any compare function is also
  2216.           restored.  The BDR_DDELETE flag is raised if not set
  2217.           already since its reloaded nodes are dynamic!
  2218.  
  2219.      int  save(const char *filename);
  2220.  
  2221.           Use this function to save a Binder in a file.  Only
  2222.           strongly type checked Binders can be streamed, i.e
  2223.           template and form instances, MBinders, or you own derived
  2224.           Binders that override the protected Dstore() and Dload()
  2225.           virtual functions.  Be sure to register the compare
  2226.           function if you want it to persist, see RegisterComP(). 
  2227.           The state of the current node setting and flags will also
  2228.           persist upon reloading.  The BDR_DSTORE flag must be set
  2229.           in order for save to proceed.  If successful, save() will
  2230.           return a non zero value.
  2231.  
  2232.      voiDV vector();
  2233.  
  2234.           Returns a dynamically allocated, NULL terminated, vector
  2235.           of void pointers to the nodes currently in the Binder. 
  2236.           The order of nodes in the Binder dictates the order of
  2237.           pointers in the vector.  "VoiDV" is typedef'ed as a
  2238.           pointer to a void pointer, i.e. "voiD *."  Remember to
  2239.           delete the returned vector when you are done with it! 
  2240.           The nodes of the Binder are not alerted to the fact that
  2241.           they are pointed to by this vector, i.e. Dattach() is not
  2242.           called - Dattach() is only called to "attach" data to a
  2243.           Binder.  Thus the Mutual nodes in an MBinder are not
  2244.           linked to the returned vector!
  2245. Class: Binder                                         binder.hpp
  2246. -----------------------------------------------------------------
  2247.  
  2248.  
  2249.      virtual ~Binder()  { destruct(); }
  2250.  
  2251.           See the entry for destruct() near the beginning of the
  2252.           protected section.
  2253.  
  2254.  
  2255. Housekeeping primitives, public:
  2256.  
  2257.      unsigned Limit()  { return limit; }
  2258.  
  2259.           Returns the number of cells (in use plus available) in
  2260.           the Binder's internal physical array.
  2261.  
  2262.      unsigned setLimit(unsigned newLimit);
  2263.  
  2264.           Adjusts the size of the Binders internal physical array
  2265.           if possible returning the new limit or zero otherwise. 
  2266.           It's not possible to set the new limit below the number
  2267.           of nodes currently in the Binder or below delta or above
  2268.           maxNodes.
  2269.  
  2270.      unsigned pack()  { return setLimit(nodes); }
  2271.  
  2272.           Shrink the Binder's internal physical array to be just
  2273.           big enough to hold the nodes.  Returns the number of
  2274.           nodes in the Binder which is also the size of the
  2275.           Binder's internal physical array if successful, otherwise
  2276.           zero is returned.  The logical array is always contiguous
  2277.           within the physical array, i.e. no holes.  The Binder may
  2278.           map the logical array's index 0 to any physical index,
  2279.           wrapping the logical array as necessary.  However, after
  2280.           a successful pack(), the logical and physical arrays are
  2281.           identical, i.e. no wrap.
  2282.  
  2283.      unsigned Delta()  { return delta; }
  2284.  
  2285.           Returns the size that the Binder will grow or shrink upon
  2286.           overflow or underflow respectively.
  2287. Class: Binder                                         binder.hpp
  2288. -----------------------------------------------------------------
  2289.  
  2290.  
  2291.      unsigned setDelta(unsigned newDelta = BDR_DELTA);
  2292.  
  2293.           Set the granularity of growth/shrinkage for the Binder's
  2294.           physical array.  BDR_DELTA is defined as 10.  Returns the
  2295.           value of the new delta if successful otherwise zero is
  2296.           returned.
  2297.  
  2298.      unsigned Nodes()  { return nodes; }
  2299.  
  2300.           Returns the number of nodes currently bound within the
  2301.           Binder, i.e. size of the logical array.
  2302.  
  2303.      unsigned MaxNodes()  { return maxNodes; }
  2304.  
  2305.           Returns the maximum number of nodes the Binder is ever
  2306.           allowed to hold.
  2307.  
  2308.      unsigned setMaxNodes(unsigned newMaxNodes = BDR_MAXNODES);
  2309.  
  2310.           Set the maximum number of nodes a Binder is allowed to
  2311.           hold returning this new value.  NewMaxNodes must not be
  2312.           below "limit" (current size of the Binder's internal
  2313.           physical array) or above BDR_MAXNODES in order to be
  2314.           valid.  Zero is returned if newMaxNodes is invalid. 
  2315.           BDR_MAXNODES is defined as ((unsigned) (UINT_MAX /
  2316.           sizeof(voiD))).  "VoiD" is typedef'ed as a pointer to
  2317.           void.
  2318.  
  2319.      unsigned vacancy()  { return maxNodes - nodes; }
  2320.  
  2321.           Returns the number of nodes that can yet be added to the
  2322.           Binder.
  2323.  
  2324.      unsigned vacancyNonElastic()  { return limit - nodes; }
  2325.  
  2326.           Returns the number of nodes that can still be added to
  2327.           the Binder without undergoing an expansion of the
  2328.           Binder's internal physical array.
  2329. Class: Binder                                         binder.hpp
  2330. -----------------------------------------------------------------
  2331.  
  2332.  
  2333.      unsigned Flags(unsigned flags = BDR_ALL_FLAGS)
  2334.                { return (this->flags & flags); }
  2335.  
  2336.           Returns the specified flag(s).  BDR_ALL_FLAGS masks all
  2337.           flags.  For flag definitions see "flags" in the private
  2338.           section of Binder.
  2339.  
  2340.      unsigned setFlags(unsigned flags)
  2341.                { return (this->flags |= flags); }
  2342.  
  2343.           Allows you to raise any Binder flag(s).  Be sure you
  2344.           don't set the BDR_DDELETE flag if the Binder has
  2345.           statically allocated nodes.  It is sometimes useful to
  2346.           sort the Binder using one compare function then scan it
  2347.           with a slightly different "filter" compare function. 
  2348.           Upon setting this new compare function the BDR_SORTED
  2349.           flag is automatically reset but you can set it again with
  2350.           setFlags()!
  2351.  
  2352.      unsigned resetFlags(unsigned flags)
  2353.                { return (this->flags &= ~flags); }
  2354.  
  2355.           Allows you to reset any Binder flag(s).  Be sure you
  2356.           don't reset the BDR_DDELETE flag if you are counting on
  2357.           the Binder's deleting any remaining nodes.  The Binder's
  2358.           destructor calls allRmv() unless the BDR_DDELETE flag is
  2359.           set in which case it calls allDel().
  2360. Class: Binder                                         binder.hpp
  2361. -----------------------------------------------------------------
  2362.  
  2363.      Binder&  operator<<(Binder& (*manipulator)(Binder&))
  2364.                { return (manipulator?
  2365.                     (*manipulator)(*this)
  2366.                     : *this); }
  2367.  
  2368.           This overloaded operator allows user defined manipulators
  2369.           to be applied to a Binder in the same fashion iomanip.h
  2370.           allows manipulators for streams.  For example:
  2371.  
  2372.                // examp701.cpp - link with binder.obj
  2373.  
  2374.                #include "binder.hpp"
  2375.  
  2376.                Binder& clear(Binder& b)
  2377.                {
  2378.                     if (b.Flags(BDR_DDELETE))
  2379.                          b.allDel();
  2380.                     else
  2381.                          b.allRmv();
  2382.                     return b;
  2383.                }
  2384.  
  2385.                char *v[] = {"line one ", "line two ", 0 };
  2386.  
  2387.                main()
  2388.                {
  2389.                     Binder b ((voiDV) v);
  2390.                     b << clear;
  2391.                     b.atIns(0,"only line");
  2392.                     while (++b)
  2393.                          cout << (char *) b.get() << "\n";
  2394.                     return 0;
  2395.                }
  2396. Class: Binder                                         binder.hpp
  2397. -----------------------------------------------------------------
  2398.  
  2399.  
  2400. Elastic Array primitives, public:
  2401.  
  2402.      voiD atIns(unsigned n, voiD D);
  2403.  
  2404.           Inserts a new cell at index n pushing back all cells
  2405.           starting at n possibly expanding the Binder's internal
  2406.           physical array if necessary.  The specified void pointer
  2407.           is copied to the new nth cell and is also returned.  If
  2408.           the operation fails the Binder is left unchanged and
  2409.           voiD0 (NULL pointer to void) is returned.  If D is NULL
  2410.           than a cell is not added.
  2411.  
  2412.      voiD atInsNew(unsigned n, const voiD D);
  2413.  
  2414.           Same as atIns() except the data pointed to by D is cloned
  2415.           via Dnew() and the pointer to the clone is inserted
  2416.           instead of the original.  The inserted pointer is
  2417.           returned if all goes well otherwise voiD0, the NULL void
  2418.           pointer, is returned.  The BDR_DNEW flag must be set to
  2419.           enable this primitive.  The default Dnew() for a typeless
  2420.           Binder always returns NULL causing this primitive to fail
  2421.           even if the BDR_DNEW flag is set. 
  2422.  
  2423.      voiD atRmv(unsigned n);
  2424.  
  2425.           Return the node pointer stored in the nth cell destroying
  2426.           this position pulling all cells starting at n + 1 forward
  2427.           perhaps collapsing the Binder's internal physical array
  2428.           if the remaining number of cells falls below an
  2429.           internally calculated threshold (lowThreshold).  If the
  2430.           operation fails, e.g. n is out of range, voiD0 (NULL
  2431.           pointer to void) is returned.
  2432.  
  2433.      void allRmv();
  2434.  
  2435.           Destroys all the cell positions discarding the node
  2436.           pointers without deleting the nodes.
  2437. Class: Binder                                         binder.hpp
  2438. -----------------------------------------------------------------
  2439.  
  2440.  
  2441.      int  atDel(unsigned n);
  2442.  
  2443.           The BDR_DDELETE flag must be set to enable this
  2444.           primitive.  Performs the same function as atRmv() except
  2445.           the discarded node is deleted via the Ddelete() protected
  2446.           virtual function.  A non zero value is returned to
  2447.           indicate success.
  2448.  
  2449.      voiD atDelAsg(unsigned n, voiD D);
  2450.  
  2451.           The BDR_DDELETE and BDR_DASSIGN flags must both be set to
  2452.           enable this primitive.  Performs the same function as
  2453.           atDel() except the contents of the node is copied to the
  2454.           D parameter location before the node is deleted.  The
  2455.           copy operation is performed by the Dassign() protected
  2456.           virtual function.  If the D parameter is NULL, or the
  2457.           Dassign() function fails, or for any other reason, e.g.
  2458.           n is out of range, etc., the primitive fails and NULL is
  2459.           returned.  Otherwise the value of the D parameter is
  2460.           returned.  The default Dassign() for a typeless Binder
  2461.           always returns a failure indication.
  2462.  
  2463.      int  allDel();
  2464.  
  2465.           Repeatedly calls atDel(0) if the BDR_DDELETE flag is set. 
  2466.           If the BDR_DDELETE flag is not set, zero is returned to
  2467.           indicate failure.
  2468.  
  2469.      voiD atPut(unsigned n, voiD D);
  2470.  
  2471.           Overwrites the nth cell's pointer with D, the new node
  2472.           pointer, returning D.  If D is NULL or n is out of range
  2473.           then nothing is overwritten and voiD0 (NULL void pointer)
  2474.           is returned.  Before the old pointer is overwritten, it
  2475.           is deleted via Ddelete if the BDR_DDELETE flag is set. 
  2476.           The subscript range of a Binder is always 0 to nodes - 1,
  2477.           hence the concept of an elastic array.
  2478.  
  2479. Class: Binder                                         binder.hpp
  2480. -----------------------------------------------------------------
  2481.  
  2482.  
  2483.      voiD atPutNew(unsigned n, const voiD D);
  2484.  
  2485.           The BDR_DNEW flag must be set to enable this primitive. 
  2486.           Performs the same function as atPut() except the data
  2487.           pointed to by D is cloned via Dnew() and the pointer to
  2488.           the clone is atPut()'ed.  If successful the clone's
  2489.           pointer is returned, otherwise NULL is returned.
  2490.  
  2491.      voiD atPutAsg(unsigned n, const voiD D);
  2492.  
  2493.           The BDR_DASSIGN flag must be set to enable this
  2494.           primitive.  Copies the data pointed to by the D parameter
  2495.           to the node bound at index n.  The actual copying is
  2496.           performed by the Dassign() protected virtual function. 
  2497.           If successful, the pointer to the bound node is returned. 
  2498.           The default Dassign() for a typeless Binder always
  2499.           returns a failure indication.
  2500.  
  2501.      voiD atGet(unsigned n);
  2502.  
  2503.           Returns the node pointer stored in the nth cell.
  2504.  
  2505.      voiD operator[](unsigned n)  { return atGet(n); }
  2506.  
  2507.           As shown by its inline definition, the overloaded
  2508.           subscripting operator provides a convenient notation for
  2509.           calling atGet().
  2510.  
  2511.      voiD atGetAsg(unsigned n, voiD D);
  2512.  
  2513.           Provides the same functionality as atGet(), except the
  2514.           contents of the node bound at index n is copied via
  2515.           Dassign() to the location pointed to by the D parameter. 
  2516.           If successful the value of the D parameter is returned. 
  2517.           The BDR_DASSIGN flag must be set to enable this
  2518.           primitive.  Also, the default Dassign() for a typeless
  2519.           Binder always returns a failure indication.
  2520. Class: Binder                                         binder.hpp
  2521. -----------------------------------------------------------------
  2522.  
  2523.  
  2524.      voiD atXchg(unsigned n, voiD D);
  2525.  
  2526.           Returns the node pointer stored in the nth cell if D is
  2527.           not NULL and n is in range otherwise voiD0 (NULL) is
  2528.           returned.  D is written to the nth cell.
  2529.  
  2530.      unsigned index(const voiD D);
  2531.  
  2532.           Find the index of the cell containing the pointer with
  2533.           the same value as D.  If D is not bound in the Binder
  2534.           then index() returns BDR_NOTFOUND which is defined as
  2535.           BDR_MAXNODES which is in turn defined as ((unsigned)
  2536.           (UINT_MAX / sizeof(voiD))).  Since the cells of a binder
  2537.           are indexed from 0 to nodes - 1, BDR_NOTFOUND will never
  2538.           be an index of a valid Binder cell.
  2539.  
  2540.      int  forEach(BDRapplY B, voiD M = voiD0, voiD A = voiD0);
  2541.  
  2542.           This Binder iterator applies the function specified by
  2543.           the function pointed to by B to each of its nodes.  M and
  2544.           A are user definable since they are simply passed on to
  2545.           the B specified function along with the pointer to the
  2546.           node being processed.  ForEach() returns true if B is not
  2547.           NULL and there are nodes to process.  The function
  2548.           pointer type is defined as:
  2549.  
  2550.                typedef void (*BDRapplY)
  2551.                     (voiD D, voiD M, voiD A);
  2552. Class: Binder                                         binder.hpp
  2553. -----------------------------------------------------------------
  2554.  
  2555.  
  2556. Stack, Queue, and Deque primitives, public:
  2557.  
  2558.      voiD push(voiD D)  { return atIns(0,D); }
  2559.  
  2560.           Pushes the node pointer onto the stack returning that
  2561.           pointer if successful otherwise it returns voiD0, the
  2562.           NULL void pointer.
  2563.  
  2564.      voiD pushNew(const voiD D)  { return atInsNew(0,D); }
  2565.  
  2566.           Same as push() except the data pointed to by D is cloned
  2567.           and a pointer to the clone is instead pushed.
  2568.  
  2569.      voiD pop()  { return atRmv(0); }
  2570.  
  2571.           Pop the stack returning the node pointer or voiD0, the
  2572.           NULL void pointer, if no nodes are present.
  2573.  
  2574.      Binder&  operator>>(voiD& D) { D = atRmv(0); return *this; }
  2575.  
  2576.           Provides a convenient way to call pop().
  2577.  
  2578.      int  popDel()  { return atDel(0); }
  2579.  
  2580.           Same as pop() except the popped pointer is deleted via
  2581.           the Ddelete() function.  The BDR_DDELETE flag must be set
  2582.           to enable this primitive.
  2583.  
  2584.      voiD popDelAsg(voiD D)  { return atDelAsg(0,D); }
  2585.  
  2586.           Same as popDel() except the contents of the node being
  2587.           delete is copied via Dassign() to the location pointed to
  2588.           by D.  If successful D is returned otherwise NULL is. 
  2589.           The BDR_DASSIGN and BDR_DDELETE flags both must be set to
  2590.           enable this primitive.
  2591.  
  2592. Class: Binder                                         binder.hpp
  2593. -----------------------------------------------------------------
  2594.  
  2595.  
  2596.      voiD top()  { return atGet(0); }
  2597.  
  2598.           Returns the pointer to the node at the front of the
  2599.           Binder or voiD0 (NULL void pointer) if there are no
  2600.           nodes.  The front of the Binder is considered to be the
  2601.           top of the stack or the front of the deque-queue.
  2602.  
  2603.      voiD topAsg(voiD D)  { return atGetAsg(0,D); }
  2604.  
  2605.           Same of top() except the contents of the top node is
  2606.           copied via the Dassign() virtual function to the location
  2607.           pointed to by D.  If successful D is returned.  The
  2608.           BDR_DASSIGN flag must be set to enable this primitive.
  2609.  
  2610.      voiD insQ(voiD D)  { return atIns(nodes,D); }
  2611.  
  2612.           Inserts the D pointer at the rear of the queue returning
  2613.           this pointer if successful or voiD0, the NULL void
  2614.           pointer, otherwise.
  2615.  
  2616.      Binder& operator<<(voiD D) { atIns(nodes,D); return *this; }
  2617.  
  2618.           This overloaded operator provides a convenient notation
  2619.           for inserting into the queue.
  2620.  
  2621.      voiD insQNew(const voiD D)  { return atInsNew(nodes,D); }
  2622.  
  2623.           Same as insQ() except what D points to is cloned via the
  2624.           Dnew() protected virtual function and the pointer to the
  2625.           clone is queued instead of D.  If successful the pointer
  2626.           to the clone is returned.  The BDR_DNEW flag must be set
  2627.           to enable this primitive.
  2628.  
  2629.      voiD unQ()  { return atRmv(nodes-1); }
  2630.  
  2631.           Pops the rear of the queue returning its pointer.
  2632.  
  2633. Class: Binder                                         binder.hpp
  2634. -----------------------------------------------------------------
  2635.  
  2636.  
  2637.      int  unQDel()  { return atDel(nodes-1); }
  2638.  
  2639.           Same as unQ() except the popped node is deleted via the
  2640.           Ddelete() virtual function.  Zero is returned to indicate
  2641.           failure.  The BDR_DDELETE flag must be set to enable this
  2642.           primitive.
  2643.  
  2644.      voiD unQDelAsg(voiD D)  { return atDelAsg(nodes-1,D); }
  2645.  
  2646.           Same as unQDel() except before deleting the popped node
  2647.           its contents are copied via the Dassign() virtual
  2648.           function to the location pointed to by D.  If successful
  2649.           D is returned.  Both the BDR_DASSIGN and BDR_DDELETE
  2650.           flags have to be set to enable this primitive.
  2651.  
  2652.      voiD rear()  { return atGet(nodes-1); }
  2653.  
  2654.           Returns a pointer to the rear node of the queue.
  2655.  
  2656.      voiD rearAsg(voiD D)  { return atGetAsg(nodes-1,D); }
  2657.  
  2658.           Same as rear() except the contents of the rear node is
  2659.           copied via the Dassign() virtual function to the location
  2660.           pointed to by D.  If successful D is returned otherwise
  2661.           NULL is returned.  The BDR_DASSIGN flag must be set to
  2662.           enable this primitive.
  2663. Class: Binder                                         binder.hpp
  2664. -----------------------------------------------------------------
  2665.  
  2666.  
  2667. List primitives, public:
  2668.  
  2669.      unsigned CurNode();
  2670.  
  2671.           Returns the index to the list's current node if there is
  2672.           one or BDR_NOTFOUND if the current node is undefined. 
  2673.           When a Binder is first constructed, its current node is
  2674.           undefined. 
  2675.  
  2676.      int  setCurNode(unsigned n = BDR_MAXNODES);
  2677.  
  2678.           Resets the list's internal current node index to n if
  2679.           within range, i.e. 0 to nodes-1, or to undefined if not. 
  2680.           BDR_MAXNODES is never in range so the current node is
  2681.           left as undefined if setCurNode() is called without a
  2682.           parameter.  Returns false only if current node is left
  2683.           undefined.
  2684.  
  2685.      voiD ins(voiD D);
  2686.  
  2687.           Inserts D into the Binder after the list's current node
  2688.           making the inserted pointer, in the newly created cell,
  2689.           the new current node.  Returns D if successful, otherwise
  2690.           it returns voiD0, the NULL void pointer.  If at the
  2691.           outset, the current node is undefined then the node
  2692.           pointer is inserted at the rear of the list and made the
  2693.           new current node.  Remember, a list may or may not have
  2694.           a current node defined.
  2695.  
  2696.      voiD insNew(const voiD D);
  2697.  
  2698.           Same as ins() except what D points to is cloned via the
  2699.           Dnew() virtual function and the pointer to the clone is
  2700.           inserted.  If successful the pointer to the clone is
  2701.           returned otherwise voiD0, the NULL void pointer, is
  2702.           returned.  The BDR_DNEW flag must be set to enable this
  2703.           primitive.
  2704. Class: Binder                                         binder.hpp
  2705. -----------------------------------------------------------------
  2706.  
  2707.  
  2708.      voiD rmv();
  2709.  
  2710.           Destroys the current node cell returning its pointer. 
  2711.           The current node setting is decremented making the node
  2712.           ahead of the node just extracted current. If the first
  2713.           node of the list is extracted the current node index is
  2714.           left unchanged.  If the first node is also the last node
  2715.           the current node becomes undefined.  If the operation
  2716.           fails for any reason, e.g. current node undefined at the
  2717.           outset or no nodes in the list, voiD0, the NULL void
  2718.           pointer, is returned.
  2719.  
  2720.      int  del();
  2721.  
  2722.           Same as rmv() except the removed node is deleted via the
  2723.           Ddelete() protected virtual function.  Zero is returned
  2724.           to indicate failure.  The BDR_DDELETE flag must be set to
  2725.           enable this primitive.
  2726.  
  2727.      voiD delAsg(voiD D);
  2728.  
  2729.           Same as del() except the contents of the node is copied
  2730.           via the Dassign() protected virtual function to the
  2731.           location pointed to by D before the node is itself is
  2732.           deleted.  If successful D is returned.  Both the
  2733.           BDR_DASSIGN and BDR_DDELETE flags have to be set to
  2734.           enable this primitive.
  2735.  
  2736.      voiD put(voiD D)  { return atPut(curNode,D); }
  2737.  
  2738.           Overwrites the current node's pointer with D, the new
  2739.           node pointer, returning D.  If D is NULL or the current
  2740.           node index is undefined then nothing is overwritten and
  2741.           voiD0 (NULL void pointer) is returned.  Before the old
  2742.           pointer is overwritten, it is deleted via Ddelete if the
  2743.           BDR_DDELETE flag is set.
  2744. Class: Binder                                         binder.hpp
  2745. -----------------------------------------------------------------
  2746.  
  2747.  
  2748.      voiD putNew(const voiD D)  { return atPutNew(curNode,D); }
  2749.  
  2750.           The BDR_DNEW flag must be set to enable this primitive. 
  2751.           Performs the same function as put() except the data
  2752.           pointed to by D is cloned via Dnew() and the pointer to
  2753.           the clone is put.  If successful the clone's pointer is
  2754.           returned, otherwise NULL is returned.
  2755.  
  2756.      voiD putAsg(const voiD D)  { return atPutAsg(curNode,D); }
  2757.  
  2758.           The BDR_DASSIGN flag must be set to enable this
  2759.           primitive.  Copies the data pointed to by the D parameter
  2760.           to the current node.  The actual copying is performed by
  2761.           the Dassign() protected virtual function.  If successful,
  2762.           the pointer to the bound node is returned.
  2763.  
  2764.      voiD get()  { return atGet(curNode); }
  2765.  
  2766.           Returns the pointer to the list's current node or voiD0,
  2767.           the NULL void pointer, if no node is current or there are
  2768.           no nodes.
  2769.  
  2770.      operator TYPE *()  { return atGet(curNode); }
  2771.  
  2772.           This implicit type cast operator provides a convenient
  2773.           notation for calling get().  It is defined in template
  2774.           and form instances where TYPE is the type of data being
  2775.           bound in the Binder.  MBinders define TYPE as Mutual. 
  2776.           Typeless Binders do not define this operator.
  2777.  
  2778.      voiD getAsg(voiD D)  { return atGetAsg(curNode,D); }
  2779.  
  2780.           Same as get() except the current node is copied via the
  2781.           Dassign() protected virtual function to the location
  2782.           pointed to by D.  If successful D is returned.  The
  2783.           BDR_DASSIGN flag must be set to enable this primitive.
  2784. Class: Binder                                         binder.hpp
  2785. -----------------------------------------------------------------
  2786.  
  2787.  
  2788.      voiD next();
  2789.  
  2790.           Advances the list's current node index returning the
  2791.           pointer to the next node.  Upon reaching the end of the
  2792.           list next() returns voiD0, the NULL void pointer.  A
  2793.           subsequent call to next() causes the current node index
  2794.           to wrap around to zero, the index of the first node in
  2795.           the list, and the process of walking the list can begin
  2796.           anew.
  2797.  
  2798.      voiD operator++()  { return next(); }
  2799.  
  2800.           This overloaded prefix operator provides a convenient
  2801.           notation for calling next().
  2802.  
  2803.      voiD nextAsg(voiD D);
  2804.  
  2805.           Same as next() except the contents of the new current
  2806.           node is copied via the Dassign() protected virtual
  2807.           function to the location pointed to by D.  If successful
  2808.           D is returned.  The BDR_DASSIGN flag must be set to
  2809.           enable this primitive.
  2810.  
  2811.      voiD prev();
  2812.  
  2813.           Decrements the list's current node index returning the
  2814.           pointer to the new current node.  After reaching the
  2815.           front of the list, the next call to prev() returns voiD0,
  2816.           the NULL void pointer.  A subsequent call to prev()
  2817.           causes the current node index to wrap around positioning
  2818.           itself on the last node of the list and the process of
  2819.           walking backwards across the list can begin anew.
  2820.  
  2821.      voiD operator--()  { return prev(); }
  2822.  
  2823.           This overloaded prefix operator provides a convenient
  2824.           notation for calling prev().
  2825.  
  2826. Class: Binder                                         binder.hpp
  2827. -----------------------------------------------------------------
  2828.  
  2829.  
  2830.      voiD prevAsg(voiD D);
  2831.  
  2832.           Same as prev() except the contents of the current node is
  2833.           copied via the Dassign() protected virtual function to
  2834.           the location pointed to by D.  If successful D is
  2835.           returned.  The BDR_DASSIGN flag must be set to enable
  2836.           this primitive.
  2837.  
  2838.      voiD firstThat(BDRdetecT B, voiD M = voiD0);
  2839.  
  2840.           This Binder iterator applies the function pointed to by
  2841.           B to each node in sequence until the function returns non
  2842.           zero.  It leaves the current node set to the node found
  2843.           returning its pointer.  If the function fails to detect
  2844.           a node the current node becomes undefined and NULL is
  2845.           returned.  The M parameter is user definable being simply
  2846.           passed on the to the B function along with the node
  2847.           pointer.  The function pointer type is defined as:
  2848.  
  2849.                typedef int (*BDRdetecT)(voiD D, voiD M);
  2850.  
  2851.      voiD lastThat(BDRdetecT B, voiD M = voiD0);
  2852.  
  2853.           This Binder iterator is similar to firstThat() except it
  2854.           scans across the Binder from the rear to the front.
  2855. Class: Binder                                         binder.hpp
  2856. -----------------------------------------------------------------
  2857.  
  2858.  
  2859. Sort and Search primitives, public:
  2860.  
  2861.      unsigned Sorted()  { return (flags & BDR_SORTED); }
  2862.  
  2863.           Returns true if the Binder is still sorted since its last
  2864.           sort.  For example, if a Binder was sorted and then nodes
  2865.           popped from it, it would still be sorted so Sorted()
  2866.           would return true.  But if it had been pushed since the
  2867.           last sort, the sorted order couldn't still be guaranteed
  2868.           so Sorted() would return false.  It is possible to
  2869.           disturb a Binder's sorted order without the Binder being
  2870.           able to detect it, e.g. using the pointer returned from
  2871.           atGet() to modify a key field of a node while it is still
  2872.           bound within the Binder.  In such cases you should call
  2873.           unSort() to let the Binder know that it can no longer
  2874.           guarantee a sorted order.
  2875.  
  2876.      void unSort()  { flags &= ~BDR_SORTED; }
  2877.  
  2878.           See explanation of Sorted().
  2879.  
  2880.      void setComP(BDRcomP comP = BDRcomP0)
  2881.                { this->comP = comP; flags &= ~BDR_SORTED; }
  2882.  
  2883.           Sets the Binder's compare function which is used by
  2884.           sort(), insSort(), insUnique(), findFirst(), etc.  The
  2885.           compare function must return zero to indicate a match and
  2886.           a value greater than zero if its first parameter is
  2887.           greater than its second.  The compare function pointer
  2888.           type is defined as:
  2889.  
  2890.                typedef int (* BDRcomP)
  2891.                     (const voiD D1, const voiD D2);
  2892.  
  2893.                #define BDRcomP0 (( BDRcomP)0)
  2894.  
  2895.      BDRcomP  ComP()  { return comP; }
  2896.  
  2897.           Returns the compare function pointer stored in the
  2898.           Binder's header.
  2899. Class: Binder                                         binder.hpp
  2900. -----------------------------------------------------------------
  2901.  
  2902.  
  2903.      int  sort(BDRcomP comP = BDRcomP0);
  2904.  
  2905.           Uses a binary sort to sort the list in conjunction with
  2906.           the user supplied compare function.  If the compare
  2907.           function pointer is NULL then the previously set compare
  2908.           function pointer is used.  If the previous function
  2909.           pointer is NULL also then the list obviously can't be
  2910.           sorted and false is returned.  The compare function
  2911.           pointer type is defined as:
  2912.  
  2913.                typedef int (* BDRcomP)
  2914.                     (const voiD D1, const voiD D2);
  2915.  
  2916.                #define BDRcomP0 (( BDRcomP)0)
  2917.  
  2918.      voiD insSort(voiD D);
  2919.  
  2920.           Uses the previously set compare function pointer to
  2921.           insert the D pointer into the Binder in a sorted order
  2922.           according to the key contained in the node pointed to by
  2923.           D.  What is or isn't a key is defined by the compare
  2924.           function.  If the list isn't sorted, it is first sorted
  2925.           and then the insertion sort of D takes place.  In either
  2926.           case the newly insert node becomes the list's new current
  2927.           node.  If either the compare function pointer or D is
  2928.           NULL then the primitive fails and voiD0, the NULL void
  2929.           pointer is returned and the current node is left
  2930.           undefined.  Otherwise D is returned.
  2931.  
  2932.      voiD insSortNew(const voiD D);
  2933.  
  2934.           Same as insSort() except what D points to is cloned via
  2935.           the Dnew() protected virtual function and the clone is
  2936.           inserted in sorted order.  If successful a pointer to the
  2937.           clone is returned.  The BDR_DNEW flag must be set to
  2938.           enable this primitive.
  2939. Class: Binder                                         binder.hpp
  2940. -----------------------------------------------------------------
  2941.  
  2942.  
  2943.      voiD insUnique(voiD D);
  2944.  
  2945.           Calls findFirst() to see if there is a match for D.  If
  2946.           not D is inserted into the Binder via insSort().
  2947.  
  2948.      voiD insUniqueNew(const voiD D);
  2949.  
  2950.           Same as insUnique() except D is cloned via Dnew() and the
  2951.           cloned inserted in sorted order.  If successful the
  2952.           pointer to the clone is returned otherwise NULL is
  2953.           returned.  The BDR_DNEW flag must be set to enable this
  2954.           primitive.
  2955.  
  2956.      voiD findFirst(const voiD K);
  2957.  
  2958.           Uses the previously set compare function to find a node
  2959.           matching K.  Since the compare function is user definable
  2960.           any field in the node can be used a key.  The compare
  2961.           function is called with the node being tested as its
  2962.           first parameter and K as its second.  Since this is the
  2963.           same compare function that is used by insSort() and
  2964.           Sort() it is best if the compare function expects K to be
  2965.           the same type of data as the node being compared.  If
  2966.           someone maintaining your code years from now tries to
  2967.           call insSort() or sort() with your compare function
  2968.           written for a K of some other data type, he/she may have
  2969.           trouble finding out what went wrong with a sort() call
  2970.           using the non symmetrical compare function!
  2971.  
  2972.           If the list is sorted then findFirst() uses a binary
  2973.           search to find the first match, otherwise it uses a
  2974.           linear search.  If all goes well the pointer to the
  2975.           matching node is returned otherwise voiD0, the NULL void
  2976.           pointer, is returned.  The current node index is set to
  2977.           that of the matching node or to undefined if no match is
  2978.           found.
  2979. Class: Binder                                         binder.hpp
  2980. -----------------------------------------------------------------
  2981.  
  2982.  
  2983.      voiD findNext(const voiD K);
  2984.  
  2985.           If the list is sorted the next node past the list's
  2986.           current node is tested for a match with K via the
  2987.           previously set user defined compare function.  If it is
  2988.           a match the current node index is advanced and a pointer
  2989.           to that node is returned.  If it isn't, findNext()
  2990.           returns voiD0, the NULL void pointer, after setting the
  2991.           current node index to undefined.  
  2992.  
  2993.           If the list isn't sorted at the outset then the list is
  2994.           scanned starting with the first node past the current
  2995.           node and continues looking until the end of the list is
  2996.           reached.  If a match is found that node becomes the new
  2997.           current node and its pointer returned, otherwise the
  2998.           current node is set to undefined and voiD0, the NULL void
  2999.           pointer, is returned.
  3000.  
  3001.           While it is possible to call findNext() after calling
  3002.           findLast() and findPrev() several times and come up with
  3003.           a match in a sorted list, be aware that for sorted lists
  3004.           the current node must be immediately ahead or within the
  3005.           contingent of matching nodes for findNext() to find them
  3006.           as expected!  In other words for sorted lists be sure to
  3007.           call findFirst() first!
  3008.  
  3009.  
  3010.      voiD findLast(const voiD K);
  3011.  
  3012.           Where as findFirst() searches for its first match closest
  3013.           to the front of the list, findLast() searches for its
  3014.           first match closest to the rear of the list.  Likewise,
  3015.           if the list is sorted a binary search is made and if not
  3016.           a linear search is made.  The current node index is set
  3017.           to the matching node and the node's pointer returned.  If
  3018.           there are no matching nodes then the current node index
  3019.           is set to undefined and voiD0, the NULL void pointer, is
  3020.           returned.
  3021. Class: Binder                                         binder.hpp
  3022. -----------------------------------------------------------------
  3023.  
  3024.  
  3025.      voiD findPrev(const voiD K);
  3026.  
  3027.           If the list is sorted the previous node in the list is
  3028.           tested for a match with K via the user defined compare
  3029.           function previously set.  If it is a match the current
  3030.           node index is decremented and a pointer to that node is
  3031.           returned.  If it isn't, findNext() returns voiD0, the
  3032.           NULL void pointer, after setting the current node index
  3033.           to undefined.  
  3034.  
  3035.           If the list isn't sorted then the list is walked
  3036.           backwards from the current node while looking for a match
  3037.           or until dropping off the front of the list.  If a match
  3038.           is found that node becomes the new current node and its
  3039.           pointer returned otherwise the current node is set to
  3040.           undefined and voiD0, the NULL void pointer, is returned.
  3041.  
  3042.      unsigned findAll(const voiD K);
  3043.  
  3044.           Using the previously user defined compare function, each
  3045.           node is checked against K and the matches tallied.  The
  3046.           current node is left undefined and the tally returned.
  3047.  
  3048.  
  3049. Class: MBinder     Mutual ID: ID_MBinder              mutual.hpp
  3050. -----------------------------------------------------------------
  3051.  
  3052.  
  3053. Being derived publicly from Mutual and privately from Binder, the
  3054. MBinder adds Mutual's behavior to a Binder.  All of the Binder's
  3055. public member functions are in essence promoted to public scope in
  3056. the MBinder but with strong type checking wrappers like a Binder
  3057. form instance.
  3058.  
  3059.  
  3060. Functions, protected:
  3061.  
  3062.      MBinder  (initVFTs) : Binder(initVFTsOnly),
  3063.                Mutual(initVFTsEtc)  {}
  3064.  
  3065.           The "default" constructor definition is straight forward. 
  3066.           See Binder:: Binder(initVFTs) and Mutual::
  3067.           Mutual(initMutual) for details.
  3068.  
  3069.      virtual  voiD Dassign(voiD D, const voiD S);
  3070.  
  3071.           Overrides Binder::Dassign() to call the
  3072.           Mutual::operator=() virtual function.
  3073.  
  3074.      virtual  voiD Dnew(const voiD D);
  3075.  
  3076.           Overrides Binder::Dnew() to call the Mutual::clone()
  3077.           virtual function.
  3078.  
  3079.      virtual  void Ddelete(voiD D);
  3080.  
  3081.           Overrides Binder::Ddelete() to check D, a Mutual
  3082.           instance, for a refCount of zero before deleting.
  3083.  
  3084.      virtual  int Dattach(voiD D)
  3085.                { return ((MutuaL)D)->link(this); }
  3086.  
  3087.           Overrides Binder::Dattach() which did nothing.  As you
  3088.           can see from its inline definition it increments the
  3089.           Mutual instance's refCount.
  3090. Class: MBinder     Mutual ID: ID_MBinder              mutual.hpp
  3091. -----------------------------------------------------------------
  3092.  
  3093.  
  3094.      virtual  void Ddetach(voiD D)
  3095.                { ((MutuaL)D)->unlink(this); }
  3096.  
  3097.           Overrides Binder::Ddetach() which did nothing.  As you
  3098.           can see from its inline definition it decrements the
  3099.           Mutual instance's refCount.  Suppose a Mutual node is
  3100.           being deleted from an MBinder stack, i.e. popDel(). 
  3101.           Since this is an MBinder, Ddetach() decrements the nodes
  3102.           refCount and then the node is passed to Ddelete for
  3103.           processing.  If the refCount is zero it's delete.  If not
  3104.           it's simply discarded because it is still owned by some
  3105.           other object.
  3106.  
  3107.      virtual  void Dstore(ostream& os, voiD D);
  3108.  
  3109.           Overrides Binder::Dstore() to call the Mutual::fput()
  3110.           virtual function.
  3111.  
  3112.      virtual  voiD Dload(istream& is);
  3113.  
  3114.           Overrides Binder::Dload() to call the stream registry
  3115.           fget() which extracts the instance's class id and
  3116.           refCount from the stream.  This id is used to look up and
  3117.           call the associated static fget() of the matching class
  3118.           derived from Mutual.
  3119.  
  3120.      static   MBindeR load(istream& is, MBindeR thiS);
  3121.  
  3122.           This load is for use with Binder:: vload() and the file
  3123.           name load constructor.
  3124.  
  3125.      virtual  void fput(ostream& os)  { store(os); }
  3126.  
  3127.           Overrides Mutual::fput() to call MBinder:: Binder::
  3128.           store().  Mutual::fput() is invoked by the stream
  3129.           registry's fput() after it inserts id and multilink
  3130.           information into the stream.  Then Binder::store() is
  3131.           called to store the Binder portion.  The stream registry
  3132.           fput() is invoked by operator<<(ostream&, Mutual&).
  3133. Class: MBinder     Mutual ID: ID_MBinder              mutual.hpp
  3134. -----------------------------------------------------------------
  3135.  
  3136.  
  3137.      static  MutuaL fget(istream& is, MutuaL InstancE)
  3138.                { return (MutuaL)load(is,(MBindeR)InstancE); }
  3139.  
  3140.           This fget() gets called from the stream registry's fget()
  3141.           to load an MBinder whenever it encounters the ID_MBinder
  3142.           id in a stream.
  3143.  
  3144. public:
  3145.  
  3146.  
  3147.      MBinder (unsigned flags = BDR_DDELETE | BDR_DSTORE,
  3148.           unsigned maxNodes = BDR_MAXNODES,
  3149.           unsigned limit = BDR_LIMIT,
  3150.           unsigned delta = BDR_DELTA) :
  3151.           Binder(flags,maxNodes,limit,delta),
  3152.           Mutual(initVFTsEtc)  {}
  3153.  
  3154.           This constructor reflects its Binder heritage except the
  3155.           default flags have been changed to allow deletion and
  3156.           storage.  The Mutual base class simply zeros out
  3157.           everything.  The Mutual id is set in the overriding
  3158.           definition of ID().
  3159.  
  3160.      MBinder (MutuaL argv[],
  3161.           unsigned argc = 0,
  3162.           unsigned flags = BDR_BIND_ONLY)
  3163.           : Binder((voiDV)argv,argc,flags),
  3164.           Mutual(initVFTsEtc)  {}
  3165.  
  3166.           Again the constructor reflects its Binder heritage.  The
  3167.           default flags remain the same as they did for the Binder
  3168.           this time.
  3169. Class: MBinder     Mutual ID: ID_MBinder              mutual.hpp
  3170. -----------------------------------------------------------------
  3171.  
  3172.  
  3173.      MBinder (const char *filename)
  3174.           : Binder(initVFTsOnly),  Mutual(initVFTsEtc)
  3175.           { (void) vload(filename,(BDRsloaD) 
  3176.                MBinder::load,this); }
  3177.  
  3178.           Here you can see how the vload() is passed the address of
  3179.           the static MBinder::load() function effectively creating
  3180.           a virtual constructor.  This function bypasses the stream
  3181.           registry, loading a Binder instead of an MBinder.  The
  3182.           nodes are still loaded as Mutual nodes owning to the
  3183.           overriding of Dload().  It is just that the MBinder's id
  3184.           and refCount are not expected ahead of the Binder.  Use
  3185.           this load constructor if, and only if, you used save() to
  3186.           store it on the stream.  This function is simply provided
  3187.           for convenience.  Without it you would have to use the
  3188.           stream extraction operator on an opened file handle to
  3189.           extract a Mutual object and then type cast it to an
  3190.           MBinder (see examp601.cpp).
  3191.  
  3192.      int  save(const char *filename)
  3193.           { return ((!RefCount())? Binder::save(filename) : 0); }
  3194.  
  3195.           Like the load constructor, save() bypasses the stream
  3196.           registry, saving a Binder instead of an MBinder.  Its
  3197.           nodes are saved as Mutual nodes owing to the overriding
  3198.           of Dstore(), however.  It is just that no MBinder id or
  3199.           refCount is inserted before the Binder on the stream by
  3200.           the stream registry.  If you use save(), you must use the
  3201.           load constructor to reload!  Notice that save() won't
  3202.           work if there are any links to the MBinder.
  3203.  
  3204.      virtual ~MBinder()  { Binder::destruct(); }
  3205.  
  3206.           See Binder:: destruct().
  3207. Class: MBinder     Mutual ID: ID_MBinder              mutual.hpp
  3208. -----------------------------------------------------------------
  3209.  
  3210.  
  3211.      inline ostream& operator<<(ostream& os, MBinder& b)
  3212.      {
  3213.           if (b.Flags(BDR_DSTORE))
  3214.                os << *(MutuaL)&b;
  3215.           return os;
  3216.      }
  3217.  
  3218.           This stream insertion operator for MBinders insures that
  3219.           the BDR_DSTORE flag is raised before invoking the stream
  3220.           registry fput() via the overloaded Mutual stream
  3221.           insertion operator.  Unlike the save() function, the id
  3222.           and refCount are inserted ahead of the Binder and its
  3223.           nodes by the stream registry.  If you save an MBinder
  3224.           with this stream insertion operator you must reload it
  3225.           with the stream extraction operator for Mutual pointers
  3226.           (see examp602.cpp).
  3227.  
  3228.      static  void RegisterClass()
  3229.           { Mutual::RegisterClass(ID_MBinder, MBinder::fget); }
  3230.  
  3231.           Registers the MBinder loader function with the stream
  3232.           registry via the Mutual::RegisterClass() function.  The
  3233.           stream registry calls this loader to reload an MBinder
  3234.           from stream.
  3235.  
  3236.      virtual unsigned ID()  { return ID_MBinder; }
  3237.  
  3238.           Overrides Mutual:: ID() returning the id of an MBinder.
  3239.  
  3240.      virtual unsigned restream();
  3241.  
  3242.           Overrides Mutual::restream() to additionally call each
  3243.           node's restream() function.
  3244. Class: Mutual                                         mutual.hpp
  3245. -----------------------------------------------------------------
  3246.  
  3247.  
  3248. Inherited by:  MBinder
  3249. Descendants: must be derived from a public Mutual hierarchy!
  3250. Friends:  StreamRegistry
  3251.  
  3252. Mutual is a conceptually abstract class that provides for mutual
  3253. ownership and streamable behavior in its descendants.  Mutual
  3254. objects are able to record multiple reference counts which can be
  3255. used to avoid accidental deletion.  A Mutual object's most
  3256. important job, however, is to be able to store itself on a stream
  3257. and later be reloaded.  Even if a Mutual object is multiply
  3258. referenced, any attempt to store the objects referencing it will
  3259. result in only one copy of the Mutual object being stored.  When
  3260. reloaded from a stream, only one copy of the Mutual instance will
  3261. be reconstructed and the multiple links will be automatically
  3262. reestablished.
  3263.  
  3264. private:
  3265.  
  3266.      voiD parenT;
  3267.  
  3268.           The first object to "link()" to the instance with a non
  3269.           NULL parent pointer while the instance's parenT is NULL
  3270.           will automatically establish a back link to that object. 
  3271.           When the back link parent "unlinks()", parenT is reset to
  3272.           NULL (see ParenT()).  This back link makes it easy for
  3273.           MBinders containing nodes that are MBinders to backwards
  3274.           navigate across the orthogonal lists.  Also, it can be
  3275.           quite handy for the Mutual nodes of a MBinder to be able
  3276.           to access local list data stored in an instance derived
  3277.           from MBinder or perhaps even member functions.
  3278.  
  3279.      unsigned refCount;
  3280.  
  3281.           Records the number of "link()s" made to instance. 
  3282.           RefCount is incremented by link() and decremented by
  3283.           unlink().
  3284. Class: Mutual                                         mutual.hpp
  3285. -----------------------------------------------------------------
  3286.  
  3287.  
  3288.      unsigned streamCount;
  3289.  
  3290.           The number of times the instance has been written to the
  3291.           stream during the current streaming operation.  Note that
  3292.           only the first time is the instance written and any
  3293.           additional attempts to write only writes a multiple
  3294.           reference mark.  StreamCount must be less than refCount
  3295.           in order for an instance to be streamed.  This is why a
  3296.           MBinder must be "link()ed" before it can be streamed (via
  3297.           the stream insertion operator and stream registry).
  3298.  
  3299.      long streamPos;
  3300.  
  3301.           Once an instance is written to a stream its position on
  3302.           the stream is recorded internally to facilitate the
  3303.           writing of the multiple reference marks on the stream.
  3304.  
  3305.      The following chart shows what happens during a streaming
  3306.      operation.  Notice that if an instance is referenced only once
  3307.      then it will only be streamed once.  The parent pointer is not
  3308.      streamed so in order for this link to persist you must insure
  3309.      that the dominate link, the first link that reaches the
  3310.      instance when storing, is the parent link so that it will be
  3311.      automatically reconstructed when "re-link()ing" the loaded
  3312.      instance.  StreamCount isn't streamed since a reloaded
  3313.      instance's streamCount is always zero.  The user's data
  3314.      resides in descendants of Mutual.  Only on the first encounter
  3315.      with a instance during a streaming operation is it necessary
  3316.      to store the user's data.  The next time only a multiple
  3317.      reference marker is written to the stream.
  3318.  
  3319.  
  3320.           Stream contents:
  3321.  
  3322.                          1st & Only     1st of Many    MultiRef
  3323.  
  3324.           ID()                x              x              0
  3325.           refCount            1              > 1
  3326.           streamCount
  3327.           streamPos                                         x
  3328.           user's data         x              x
  3329. Class: Mutual                                         mutual.hpp
  3330. -----------------------------------------------------------------
  3331.  
  3332.  
  3333.      When the stream is subsequently reloaded, the first time an
  3334.      instance is encountered, the stream registry will load its id. 
  3335.      If its id is non zero then refCount is read and the id is used
  3336.      to look up the Mutual class' descendant's static fget()
  3337.      function, which is called to load the descendant's data.
  3338.      RegisterClass() registered this fget() function with the
  3339.      registry.  The descendant's fget() function will call its
  3340.      default constructor which in turn calls Mutual(initMutual)
  3341.      which initializes parenT to NULL and refCount, streamCount,
  3342.      and streamPos to zero.  If the refCount read from the stream
  3343.      is greater than one then the pointer to the loaded Mutual
  3344.      instance returned from the descendant's fget() function is
  3345.      also saved in a holding pen (internal to the registry) along
  3346.      with the reloaded refCount and the instance's position on the
  3347.      stream.  The pointer to the instance is returned by the
  3348.      overloaded stream extraction operator, i.e.
  3349.      operator>>(istream&,MutuaL&) as usual.
  3350.  
  3351.      If the id is zero then the instance has already been loaded
  3352.      and its pointer is being held in the holding pen of the stream
  3353.      registry.  In this case streamPos is read from the stream's
  3354.      multiple reference mark and used to uniquely identify the
  3355.      instance being held in the holding pen.  Instead of calling
  3356.      the descendant's fget() function, the pointer retrieved from
  3357.      the holding pen is returned by the stream extraction operator. 
  3358.      When the last multiple reference link to a instance held in
  3359.      the holding pen is reconstructed, the holding pen
  3360.      automatically releases the instance.  The holding pen is
  3361.      simply a Binder within the stream registry.
  3362. Class: Mutual                                         mutual.hpp
  3363. -----------------------------------------------------------------
  3364.  
  3365.  
  3366. protected:
  3367.  
  3368.      Mutual  (initMutual);
  3369.  
  3370.           The constructor zeros parenT, refCount, streamCount and
  3371.           streamPos.  The parameter is a dummy to set a pattern for
  3372.           unique descendant default constructors.  The purpose of
  3373.           this default constructor chaining in descendants is to
  3374.           provide a way for the fget() functions to separate
  3375.           construction from initialization.  This allows the top
  3376.           tier fget() to initialize virtual function tables, etc. 
  3377.           The reason it doesn't initialize any descendant data is
  3378.           because in a hierarchy of descendants, the fputs()'s and
  3379.           fget()'s may choose to store/load an ancestor's data
  3380.           before/after its own.  If this constructor were to
  3381.           initialize data in a descendant this option wouldn't be
  3382.           available!  Nor could each tier be responsible for
  3383.           insertion/extraction of its respective data to/from a
  3384.           stream.  Please note that once the virtual function table
  3385.           is initialized the virtual functions become operative and
  3386.           ID() returns its correct value for any descendants!
  3387.  
  3388.      static void serror(const char *msg, unsigned id);
  3389.  
  3390.           The stream error function is static so that it can be
  3391.           called by the static fget() function.  Being static an id
  3392.           must be supplied as a parameter since there is no
  3393.           instance implied from which id can be retrieved.  If
  3394.           streamDebug is non zero the message is streamed to cerr.
  3395.  
  3396.      virtual void error(const char *msg);
  3397.  
  3398.           Stream error function typically called by fput(), reports
  3399.           the message on cerr if streamDebug is non zero.
  3400.  
  3401.      virtual void warn(const char *msg);
  3402.  
  3403.           Stream warning function typically called by fput(),
  3404.           reports the message on cerr if streamDebug is non zero.
  3405. Class: Mutual                                         mutual.hpp
  3406. -----------------------------------------------------------------
  3407.  
  3408.  
  3409.      virtual void fput(ostream& os);
  3410.  
  3411.           Called by the stream registry to store the data in
  3412.           descendants of Mutual.
  3413.  
  3414.      static MutuaL fget(istream& is, MutuaL InstancE)
  3415.                { return InstancE; }
  3416.  
  3417.           Mutual::fget() does nothing.  It is used to set a pattern
  3418.           for a chain of fget()'s in derived classes.  A derived
  3419.           class' fget() is called by the stream registry to load an
  3420.           instance of that descendant if its InstancE parameter is
  3421.           NULL.  It must then dynamically construct a default
  3422.           instance of itself using the chain of default
  3423.           constructors discussed earlier.  It then proceeds to
  3424.           extract its class' tier data from stream and then calls
  3425.           the base class fget()'s with non NULL InstancE parameters
  3426.           to load their respective tier data.  So you see an fget()
  3427.           function tests its InstancE parameter to see whether it
  3428.           was called from the stream registry as the top tier or
  3429.           from a tier from above in which case it doesn't call the
  3430.           chain of constructors.
  3431. Class: Mutual                                         mutual.hpp
  3432. -----------------------------------------------------------------
  3433.  
  3434.  
  3435. public:
  3436.  
  3437.      static int refDebug;
  3438.  
  3439.           Turns on refCount inc/dec and over/under - flow messages
  3440.           to cerr (off by default).
  3441.  
  3442.      static int streamDebug;
  3443.  
  3444.           Turns on stream debugging messages to cerr (off by
  3445.           default).
  3446.  
  3447.      static char memberTermChar;
  3448.  
  3449.           Defines the character used to delimit member fields of an
  3450.           instance on the stream.
  3451.  
  3452.      Though not members of Mutual proper, the following stream
  3453.      manipulators are used to insert/extract the memberTermChar.
  3454.  
  3455.           ostream& Mendm(ostream& os);
  3456.           istream& Mnextm(istream& is);
  3457.  
  3458.      Mutual   (Mutual&)  { Mutual(initVFTsEtc); }
  3459.  
  3460.           Like the default constructor it calls, this copy
  3461.           initializer constructor is used for building chains of
  3462.           copy initializer constructors in its descendants.  The
  3463.           clone() function invokes the copy initializer using the
  3464.           new operator.  The clone function is typically called by
  3465.           MBinder:: Dnew() which in turn is called by MBinder
  3466.           primitives having "New" suffixes, e.g. atInsNew().
  3467.  
  3468.      static   void RegisterClass(unsigned id,
  3469.                MutuaL (*fgeT)(istream&, MutuaL));
  3470.  
  3471.           Called by the descendant class static function of the
  3472.           same name before attempting to stream instances of that
  3473.           class.  There is no need to register any intermediate
  3474.           classes, only those with instances that are streamed.
  3475. Class: Mutual                                         mutual.hpp
  3476. -----------------------------------------------------------------
  3477.  
  3478.  
  3479.      virtual  int operator=(Mutual&)  { return 0; }
  3480.  
  3481.           This function is typically called by MBinder:: Dassign()
  3482.           which in turn is called by MBinder primitives having
  3483.           "Asg" suffixes, e.g. nextAsg().  Returning zero indicates
  3484.           failure effectively turning this function "off."  If
  3485.           Dassign() receives a failure indication it passes it on
  3486.           to its calling function, e.g. nextAsg(), which then
  3487.           returns failure.
  3488.  
  3489.      virtual  MutuaL clone() { return MutuaL0; }
  3490.  
  3491.           This function is overridden in all derived classes to
  3492.           call their copy initializer constructors.  Thus clone()
  3493.           behaves like a virtual copy initializer for the Mutual
  3494.           polymorphic cluster of classes.  The clone function is
  3495.           typically called by MBinder:: Dnew() which in turn is
  3496.           called by MBinder primitives having "New" suffixes, e.g.
  3497.           atInsNew(), thus allowing heterogeneous nodes to be
  3498.           cloned on the fly in the same MBinder!
  3499.  
  3500.      voiD ParenT()  { return parenT; }
  3501.  
  3502.           The parenT pointer is automatically set to the first
  3503.           linking parent that identifies itself and reset to NULL
  3504.           if that parent identifies itself when unlinking, see
  3505.           link() and unlink().
  3506.  
  3507.      virtual unsigned ID()  { return ID_Mutual; }
  3508.  
  3509.           Overridden in all descendant classes to return its unique
  3510.           id.
  3511. Class: Mutual                                         mutual.hpp
  3512. -----------------------------------------------------------------
  3513.  
  3514.  
  3515.      virtual unsigned restream();
  3516.  
  3517.           Resets streamCount and streamPos to zero so that another
  3518.           store on stream operation can be made.  StreamCount and
  3519.           streamPos are used by the automatic multiple storage
  3520.           mechanism.  StreamCount doesn't allow the instance to be
  3521.           streamed out more times than it has been notified that it
  3522.           is referenced, i.e. refCount via link().  StreamCount
  3523.           also tells the stream registry if this is the first time
  3524.           the instance is being streamed.  If so the descendant's
  3525.           data is streamed out via Mutual::fput() after the stream
  3526.           registry outputs the id and refCount.  If not the first
  3527.           time, the multiple reference mark, ID_MRef, is streamed
  3528.           out instead of the id and streamPos is stream out
  3529.           recording the position on the stream of the actual
  3530.           instance.  After streaming a network of Mutual instances,
  3531.           streamCount should equal refCount for each instance owing
  3532.           to the fact that refCount should be the number of ways an
  3533.           instance can be reached over the network, i.e. the number
  3534.           of link()s.  Restream() reports the difference between
  3535.           refCount and streamCount before resetting streamCount and
  3536.           streamPos whenever streamPos is non zero.  If streamDebug
  3537.           is non zero the discrepancy is reported on cerr.  If
  3538.           streamPos is zero, restream() returns zero since no
  3539.           streaming has taken place since the last restream() or
  3540.           constructor call.  See Restream() for the StreamRegistry.
  3541.  
  3542.      void unlink(voiD P = voiD0);
  3543.  
  3544.           Decrements refCount if non zero, returning its value.  If
  3545.           refDebug is turned on, the refCount value, before
  3546.           decrementing, and any underflow is reported on cerr.  If
  3547.           P is equal to parenT then parenT is reset to voiD0, the
  3548.           NULL void pointer.  Unlink() is called by MBinder::
  3549.           Ddetach() which in turn is called by MBinder unbinding
  3550.           primitives, e.g. atDel(), atPut(), and atXchg(), for
  3551.           automatic unlinking of Mutual nodes.
  3552. Class: Mutual                                         mutual.hpp
  3553. -----------------------------------------------------------------
  3554.  
  3555.  
  3556.      int link(voiD P = voiD0);
  3557.  
  3558.           Increments refCount if less than UINT_MAX to indicate
  3559.           additional references in memory to the instance.  If
  3560.           refDebug is non zero then a message is streamed to cerr
  3561.           reporting the new refCount value and the address of the
  3562.           instance so that multiple linking can be traced. 
  3563.           Overflow errors are likewise reported on cerr.  If P is
  3564.           not NULL and parenT is, P is assigned to parenT.  Link()
  3565.           is called by MBinder:: Dattach() which in turn is called
  3566.           by MBinder binding primitives, e.g. atIns(), atGet(), and
  3567.           atXchg(), for automatic linking of Mutual nodes.
  3568.  
  3569.      unsigned RefCount()  { return refCount; }
  3570.  
  3571.           Called by MBinder:: Ddelete() to see if a Mutual node can
  3572.           be safely deleted.  Only when zero is returned is it okay
  3573.           to delete a Mutual node.  See link() and unlink().
  3574.  
  3575.      long StreamPos()  { return streamPos; }
  3576.  
  3577.           Provided for building Mutual object file indexes.  After
  3578.           streaming out and before restream() is called, call
  3579.           StreamPos() to determine the position of the Mutual
  3580.           object in the file.
  3581.  
  3582.      virtual ~Mutual()  {}
  3583.  
  3584.           Provided so that descendants can be virtually destroyed.
  3585.  
  3586.      Though not members of Mutual proper, the following stream
  3587.      operators are used to insert and extract Mutual instances and
  3588.      pointers respectively (see StreamRegistry).
  3589.  
  3590.           inline ostream& operator<<(ostream& os, Mutual& m)
  3591.                { StreamReg.fput(os,m); return os; }
  3592.  
  3593.           inline istream& operator>>(istream& is, MutuaL& M)
  3594.                { StreamReg.fget(is,M); return is; }
  3595. Class: StreamRegistry                                 mutual.hpp
  3596. -----------------------------------------------------------------
  3597.  
  3598.  
  3599.      The StreamRegistry provides the mechanism for allowing
  3600.      instances of classes derived from Mutual to be written out to
  3601.      a stream and later read back into memory from the stream. 
  3602.      There is only one instance of StreamRegistry.  Each class
  3603.      derived from Mutual defines its own RegisterClass() static
  3604.      member function which calls the this instance for you.  The
  3605.      following macros make the StreamRegistry instance transparent
  3606.      to the programmer.
  3607.  
  3608.      #define Restream()  StreamReg.restream()
  3609.  
  3610.           The Restream() macro expands into a call to the
  3611.           restream() member function without the need for a
  3612.           instance name.  When Mutual instances are reloaded from
  3613.           a stream back into memory, instances with multiple
  3614.           references are loaded only once while pointers to these
  3615.           instances are held in a holding pen within the
  3616.           StreamRegistry so that the multiple references can be
  3617.           reconstructed as they are encountered.  Once the stream
  3618.           is fully loaded all instances should have already been
  3619.           automatically released from the holding pen.  Restream()
  3620.           releases any stragglers, reporting the relinking
  3621.           discrepancies on cerr if StreamRegistry:: debug is set. 
  3622.           Restream() reports the number of stragglers.  You should
  3623.           call Restream() after every reloading operation to insure
  3624.           that holding pen is clear.  StreamRegistry:: debug is
  3625.           reset by default.
  3626.  
  3627.      #define RegisterFunction(id,fnC)  \
  3628.           StreamReg.registerFunction(id,(GenericFnC)fnC)
  3629.  
  3630.           Use this macro to you register the function pointers you
  3631.           want to stream in much the same way you registered
  3632.           classes.  Please note that some CPU architectures allow
  3633.           C++ compilers to generator different size function
  3634.           pointers, e.g. class member function pointers verses
  3635.           standard function pointers.  This macro will correctly
  3636.           register any function pointer that is the same size as
  3637.           the function pointer specified by the following typedef.
  3638.  
  3639.                typedef void (*GenericFnC)();
  3640. Class: StreamRegistry                                 mutual.hpp
  3641. -----------------------------------------------------------------
  3642.  
  3643.  
  3644.      #define fnC2ID(fnC)  StreamReg.fnCID((GenericFnC)fnC)
  3645.  
  3646.           Once registered, use this macro to convert a function
  3647.           pointer to its id.
  3648.  
  3649.      #define ID2fnC(fnCType,id)  (fnCType) StreamReg.fnCLU(id)
  3650.  
  3651.           Once registered, use this macro to convert an id to its
  3652.           function pointer.  The "fnCType" is the type of your
  3653.           original function pointer.
  3654.  
  3655.      The following example tests the function pointer macros.
  3656.  
  3657.  
  3658.           // examp702.cpp - link with mutual.obj and binder.obj
  3659.  
  3660.           #include "mutual.hpp"
  3661.  
  3662.           #define ID_test  5
  3663.  
  3664.           char * test(char * s1, char *s2)
  3665.                { cout << s1; return s2; }
  3666.  
  3667.           main()  {
  3668.                RegisterFunction(ID_test,test);
  3669.                cout <<(*ID2fnC(char *(*)(char *, char *),
  3670.                     fnC2ID(test)))
  3671.                     ("\nGoodbye persistence headaches!\n",
  3672.                     "Hello streamable functions!\n");
  3673.                return 0;
  3674.           }
  3675.  
  3676.  
  3677.      First the function pointer is registered.  Then the function
  3678.      pointer is converted to an id which is converted back to a
  3679.      function pointer which is invoked with the two string
  3680.      parameters returning a string to stream to cout.  You can use
  3681.      the fnC2ID() macro to save a function pointer's id inside your
  3682.      overriding Mutual::fput() function.  Inside fget(), the
  3683.      extracted id is converted back to a function pointer with the
  3684.      ID2fnC() macro.
  3685. Class: StreamRegistry                                 mutual.hpp
  3686. -----------------------------------------------------------------
  3687.  
  3688.  
  3689.      #define ForgetRegistrations()  \
  3690.           StreamReg.forgetRegistrations()
  3691.  
  3692.           When you no longer need the class and function pointer
  3693.           registrations, use this macro to clear the stream
  3694.           registry.
  3695.  
  3696.      Though not members of the StreamRegistry proper, the following
  3697.      stream operators are used to insert and extract Mutual
  3698.      instances and pointers respectively.
  3699.  
  3700.           inline ostream& operator<<(ostream& os, Mutual& m)
  3701.                { StreamReg.fput(os,m); return os; }
  3702.  
  3703.           inline istream& operator>>(istream& is, MutuaL& M)
  3704.                { StreamReg.fget(is,M); return is; }
  3705.                             Index
  3706.  
  3707.  
  3708. ~Binder()  58, 64, 66
  3709. ~MBinder()  90
  3710. ~Mutual()  100
  3711. allDel()  17, 57, 64, 71
  3712. allRmv()  17, 26, 70
  3713. assignment versus
  3714.      initialization  47
  3715. assignment  12, 25, 47, 55
  3716. AT&T  9
  3717. atDel()  17, 71
  3718. atDel
  3719. Asg()  17, 71
  3720. atGet()  17, 72
  3721. atGetAsg()  17, 72
  3722. atIns()  17, 70
  3723. atInsNew()  17, 70
  3724. atPut()  17, 71
  3725. atPutAsg()  17, 72
  3726. atPutNew()  17, 72
  3727. atRmv()  17, 70
  3728. atXchg()  17, 73
  3729. back link  92, 93
  3730. bags  22
  3731. BASE  42
  3732. BDR_ALL_FLAGS  68
  3733. BDR_BIND_ONLY  57
  3734. BDR_DASSIGN  25, 57
  3735. BDR_DDELETE  25, 57
  3736. BDR_DELTA  64
  3737. BDR_DNEW  25, 57
  3738. BDR_DSTORE  57
  3739. BDR_LIMIT  64
  3740. BDR_MAXNODES  64
  3741. BDR_NOTFOUND  73
  3742. BDR_SORTED  57
  3743. BDRapplY  73
  3744. BDRcomP  82
  3745. BDRdetecT  81
  3746. BDRendm  35, 63
  3747. BDRnextm  35, 63
  3748. berror()  38, 60
  3749. Binder()  58, 64-65
  3750. BindeR0  62
  3751. chain  43, 96
  3752. CLASS  42
  3753. classroom  3
  3754. clone  25
  3755. clone() 47, 98
  3756. cloning  25, 44, 98
  3757. commerical class
  3758.                                         designers  7
  3759. comP  57
  3760. ComP()  22, 82
  3761. compare function
  3762.                                         registration  32
  3763. comPID()  57
  3764. comPLU()  57
  3765. comPv  57
  3766. construction versus
  3767.                                         initialization  43
  3768. constructor address  43
  3769. conventional  7
  3770. cookbook  14
  3771. copy initializer 47
  3772. copyright  1, 3
  3773. CPTR  42
  3774. curNode  56
  3775. CurNode()  20, 77
  3776. current node index  20
  3777. Dassign()  14, 26, 28, 58, 87
  3778. Dattach()  59, 87
  3779. Ddelete()  14, 26, 29, 59, 87
  3780. Ddetach()  60, 88
  3781. debug  51
  3782. default constructor  43
  3783. del()  20, 78
  3784. delAsg()  20, 78
  3785. deleting  26, 29
  3786. delta  56
  3787. Delta()  17, 66
  3788. deque  19, 74
  3789. destruct()  58
  3790. Dload()  14, 33, 36, 61, 88
  3791. Dnew()  14, 26, 28, 59, 87
  3792. Dstore()  14, 33, 36, 60, 88
  3793. elastic-array  17, 70
  3794. error()  95
  3795. examp201  11
  3796. examp202  12
  3797. examp203  13
  3798. examp204  14
  3799. examp301  18
  3800. examp302  19
  3801. examp304  22
  3802. examp401  25
  3803. examp402  26
  3804. examp403  29
  3805. examp501  31
  3806. examp502  34
  3807. examp503  36
  3808. examp504  37
  3809. examp601  48
  3810. examp602  52
  3811. examp701  69
  3812. examp702  102
  3813. examples.cpp  9
  3814. extensible  43
  3815. fbinder  13-14
  3816. FBinder  13, 56
  3817. FBindeR  13, 56
  3818. fget()  45, 89
  3819. FIFO  19
  3820. findAll()  22, 86
  3821. findFirst()  22, 84
  3822. findLast()  22, 85
  3823. findNext()  22, 85
  3824. findPrev()  22, 86
  3825. first  56
  3826. firstThat()  20, 81
  3827. flags  57
  3828. Flags()  68
  3829. flat  7, 17
  3830. fmutual  41
  3831. fnC2ID()  101
  3832. FOLO  19
  3833. forEach()  17, 73
  3834. ForgetComPs()  63
  3835. ForgetRegistrations()  103
  3836. Form Cookbook  14
  3837. Form "Templates"  13
  3838. forms  13-14
  3839. fput()  44, 88, 96
  3840. FType  13, 56
  3841. function pointers  63, 101-102
  3842. GenericFnC  101
  3843. get()  20, 79
  3844. getAsg()  20, 79
  3845. granularity  7, 17
  3846. holding pen  51
  3847. Hybrid Container  17
  3848. hysteresis  7, 17
  3849. ID()  91, 93
  3850. ID_MBinder  89
  3851. ID_MRef  99
  3852. ID2fnC()  101
  3853. implict type cast  38, 79
  3854. index()  17, 73
  3855. initData()  44, 57
  3856. initialization versus
  3857.      construction  43
  3858. initialization versus
  3859.                                         assignment  47
  3860. initMutual  95
  3861. initVFTs  58
  3862. initVFTsEtc  97
  3863. initVFTsOnly  87
  3864. ins()  20, 77
  3865. insNew()  20, 77
  3866. insQ()  19, 75
  3867. insQNew()  19, 75
  3868. insSort()  22, 83
  3869. insSortNew()  22, 83
  3870. installation  9
  3871. insUnique()  22, 84
  3872. insUniqueNew()  22, 84 
  3873. lastThat()  20, 81
  3874. libsrc.cpp  9
  3875. license agreement 3
  3876. LIFO  19
  3877. limit  56
  3878. Limit()  17, 66
  3879. link()  99, 100
  3880. linkS  56
  3881. list  20, 77
  3882. load()  62, 88
  3883. lowLimit  56
  3884. lowThreshold  56
  3885. makefile  9
  3886. manipulator  69
  3887. maxNodes  56
  3888. MaxNodes()  17, 67
  3889. MBinder()  87, 89, 90
  3890. memberTermChar  35, 63, 97
  3891. Mendm  97
  3892. Mnextm  97
  3893. multiple references  41, 53,
  3894. 94, 99
  3895. Mutual()  95, 97
  3896. MutuaL0  98
  3897. mutually owned  41
  3898. next()  20, 80
  3899. nextAsg()  20, 80
  3900. nodes  56
  3901. Nodes()  17, 67
  3902. OEM  7
  3903. operator TYPE *()  38,  79
  3904. operator--()  20, 80
  3905. operator++()  20, 80
  3906. operator<<()  19, 69, 75, 91,
  3907. 100, 103
  3908. operator=()  98
  3909. operator>>()  19, 74, 94, 100,
  3910. 103
  3911. operator[]()  17, 72
  3912. pack()  17, 66
  3913. parameterize  41
  3914. parenT  92
  3915. ParenT()  98
  3916. permission  3
  3917. persistence  31
  3918. polymorphic cluster  41
  3919. pop()  19, 74
  3920. popDel()  19, 74
  3921. popDelAsg()  19, 74
  3922. portability  3
  3923. post card  4
  3924. prev()  20, 80
  3925. prevAsg()  20, 81
  3926. priority queue
  3927. push()  19, 74
  3928. pushNew()  19, 74
  3929. put()  20, 78
  3930. putAsg()  20, 79
  3931. putNew()  20, 79
  3932. queue  19, 74
  3933. rear()  19, 76
  3934. rearAsg()  19, 76
  3935. refCount  92, 94, 99
  3936. RefCount()  100
  3937. refDebug  51, 97
  3938. RegisterClass()  91, 97
  3939. RegisterComP()  63
  3940. RegisterFunction()  101
  3941. registration  4
  3942. resetFlags()  68
  3943. restream()  91, 99
  3944. Restream()  51, 101
  3945. rmv()  20, 78
  3946. royalty  3
  3947. save()  65, 90
  3948. sberror()  60
  3949. search  22, 82
  3950. self cleaning  53
  3951. sequential  20
  3952. serror()  95
  3953. setComP()  22, 82
  3954. setCurNode()  20, 77
  3955. setDelta()  17, 67
  3956. setFlags()  68
  3957. setLimit()  17, 66
  3958. setMaxNodes()  17, 67
  3959. sets  22
  3960. smart linker/librarians  9
  3961. sort  22, 82
  3962. sort()  22, 83
  3963. Sorted()  22, 82
  3964. stack  19, 74
  3965. store()  61
  3966. stream error logging  38, 95
  3967. streamCount  93-94, 99
  3968. streamDebug  38, 51, 63, 97
  3969. streamPos  93-94, 99
  3970. StreamPos()  100
  3971. StreamReg  101
  3972. StreamRegistry versus
  3973.                                         Binder streaming  51-52,
  3974. 90-91
  3975. StreamRegistry 101
  3976. subscripts  72
  3977. switches  51
  3978. symmetrical  84
  3979. tbinder  12
  3980. TBINDER()  12, 55
  3981. technical support  4
  3982. template  12
  3983. template binders  12
  3984. tier  43, 46, 96
  3985. top()  19, 75
  3986. topAsg()  19, 75
  3987. type checking  11
  3988. typeless Binders  11
  3989. unconventional  7, 17
  3990. unique  22, 82
  3991. unlink()  99
  3992. unQ()  19, 75
  3993. unQDel()  19, 76
  3994. unQDelAsg()  19, 76
  3995. unSort()  22, 82
  3996. vacancy()  17, 67
  3997. vacancyNonElastic()  17, 67
  3998. vector()  17, 65
  3999. virtual constructor  90
  4000. vload()  62, 65
  4001. voiD  64
  4002. voiD0  70
  4003. voiDV  65
  4004. warn()  95
  4005. warranty  4
  4006.